129 lines
5.9 KiB
Go
129 lines
5.9 KiB
Go
package handler
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"past/internal/config"
|
|
"past/internal/httpapi"
|
|
"past/internal/store"
|
|
"past/internal/testdb"
|
|
)
|
|
|
|
func permissionCtx(t *testing.T, method, uri, body string, steps []testdb.Step) (*httpapi.Ctx, *httptest.ResponseRecorder, *testdb.Script) {
|
|
t.Helper()
|
|
db, s := testdb.Open(t, steps)
|
|
out := httptest.NewRecorder()
|
|
g, _ := gin.CreateTestContext(out)
|
|
g.Request = httptest.NewRequest(method, uri, strings.NewReader(body))
|
|
g.Request.Header.Set("Authorization", "Bearer test")
|
|
g.Request.Header.Set("Content-Type", "application/json")
|
|
app := &httpapi.App{DB: &store.DB{DB: db}, Cfg: &config.Config{}}
|
|
return &httpapi.Ctx{G: g, R: g.Request, W: out, DB: app.DB, App: app, Method: method}, out, s
|
|
}
|
|
func uidStep(uid int64) testdb.Step {
|
|
return testdb.Step{Match: "FROM user_tokens", Columns: []string{"user_id"}, Rows: [][]driver.Value{{uid}}}
|
|
}
|
|
func cidStep(cid int64) testdb.Step {
|
|
return testdb.Step{Match: "SELECT couple_id FROM users WHERE id = ?", Columns: []string{"couple_id"}, Rows: [][]driver.Value{{cid}}}
|
|
}
|
|
func TestAgreeStartsCoolingWithoutUnbinding(t *testing.T) {
|
|
c, out, s := permissionCtx(t, "POST", "/api/unbindRespond", `{"agree":true}`, []testdb.Step{
|
|
uidStep(2), cidStep(10),
|
|
{Match: "FROM couples WHERE id=? FOR UPDATE", Columns: []string{"id", "status"}, Rows: [][]driver.Value{{int64(10), "active"}}},
|
|
{Match: "FROM users WHERE id=? AND couple_id=?", Columns: []string{"id"}, Rows: [][]driver.Value{{int64(2)}}},
|
|
{Match: "status='pending' AND requester_id!=?", Columns: []string{"id"}, Rows: [][]driver.Value{{int64(8)}}},
|
|
{Match: "cooling_until=DATE_ADD(NOW(),INTERVAL 72 HOUR)", Args: []any{int64(8)}},
|
|
})
|
|
unbindRespond(c)
|
|
if out.Code != 200 || s.Commits != 1 {
|
|
t.Fatalf("code=%d commits=%d body=%s", out.Code, s.Commits, out.Body.String())
|
|
}
|
|
}
|
|
func TestEitherPartnerCanCancelCoolingButNotAfterDeadline(t *testing.T) {
|
|
for _, expired := range []bool{false, true} {
|
|
t.Run(map[bool]string{false: "partner cancels", true: "deadline reached"}[expired], func(t *testing.T) {
|
|
rows := [][]driver.Value{{int64(8)}}
|
|
if expired {
|
|
rows = nil
|
|
}
|
|
steps := []testdb.Step{uidStep(2), cidStep(10), {Match: "FOR UPDATE", Columns: []string{"id", "status"}, Rows: [][]driver.Value{{int64(10), "active"}}}, {Match: "FROM users WHERE id=?", Columns: []string{"id"}, Rows: [][]driver.Value{{int64(2)}}}, {Match: "status='cooling' AND cooling_until>NOW()", Columns: []string{"id"}, Rows: rows}}
|
|
if !expired {
|
|
steps = append(steps, testdb.Step{Match: "status='cancelled'"})
|
|
}
|
|
|
|
c, out, s := permissionCtx(t, "POST", "/api/unbindCancel", `{}`, steps)
|
|
unbindCancel(c)
|
|
want := 200
|
|
if expired {
|
|
want = 409
|
|
if s.Commits != 0 {
|
|
t.Fatal("expired cancellation committed")
|
|
}
|
|
}
|
|
if out.Code != want {
|
|
t.Fatalf("code=%d body=%s", out.Code, out.Body.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
func TestLegacyDetailsStillRequireCurrentMemberAuthors(t *testing.T) {
|
|
for _, tt := range []struct {
|
|
name, predicate string
|
|
handler httpapi.HandlerFunc
|
|
}{
|
|
{"diary", "d.created_by IN", diaryDetail},
|
|
{"timeline", "WHERE id = ? AND created_by IN", timelineDetail},
|
|
{"checkin", "c.created_by IN", checkinDetail},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c, out, _ := permissionCtx(t, "GET", "/api/detail?id=9", "", []testdb.Step{uidStep(1), cidStep(22), {Match: "SELECT user1_id, user2_id FROM couples WHERE id = ?", Columns: []string{"user1_id", "user2_id"}, Rows: [][]driver.Value{{int64(1), int64(3)}}}, {Match: tt.predicate, Columns: []string{"id"}}})
|
|
tt.handler(c)
|
|
if out.Code != 404 {
|
|
t.Fatalf("missing or unrelated detail returned %d: %s", out.Code, out.Body.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLegacyMemberRecordsRemainReadableAcrossHistoricalSpaceIDs(t *testing.T) {
|
|
for _, tt := range []struct {
|
|
name, predicate string
|
|
handler httpapi.HandlerFunc
|
|
}{
|
|
{"timeline", "FROM timeline WHERE id = ? AND created_by IN", timelineDetail},
|
|
{"photos", "WHERE a.created_by IN (?, ?) AND p.album_id = ?", photos},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c, out, _ := permissionCtx(t, "GET", "/api/detail?id=9&album_id=9", "", []testdb.Step{
|
|
uidStep(1), cidStep(22),
|
|
{Match: "SELECT user1_id, user2_id FROM couples WHERE id = ?", Columns: []string{"user1_id", "user2_id"}, Rows: [][]driver.Value{{int64(1), int64(3)}}},
|
|
{Match: tt.predicate, Columns: []string{"id", "couple_id", "created_by", "path"}, Rows: [][]driver.Value{{int64(9), int64(10), int64(1), "uploads/users/1/photos/a.jpg"}}},
|
|
})
|
|
tt.handler(c)
|
|
if out.Code != 200 || !strings.Contains(out.Body.String(), "uploads/users/1/photos/a.jpg") {
|
|
t.Fatalf("legacy record disappeared: code=%d body=%s", out.Code, out.Body.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLegacyPeriodReadsSharedTablesInsteadOfPersonalTables(t *testing.T) {
|
|
c, out, _ := permissionCtx(t, "GET", "/api/periodList", "", []testdb.Step{
|
|
uidStep(1), cidStep(10),
|
|
{Match: "SELECT period_visible FROM users", Columns: []string{"period_visible"}, Rows: [][]driver.Value{{int64(1)}}},
|
|
{Match: "SELECT id, period_visible FROM users WHERE couple_id", Columns: []string{"id", "period_visible"}, Rows: [][]driver.Value{{int64(2), int64(1)}}},
|
|
{Match: "SELECT user1_id, user2_id, period_source_user_id", Columns: []string{"user1_id", "user2_id", "period_source_user_id"}, Rows: [][]driver.Value{{int64(1), int64(2), int64(2)}}},
|
|
{Match: "FROM period_records WHERE couple_id = ?", Args: []any{int64(10)}, Columns: []string{"id", "user_id"}, Rows: [][]driver.Value{{int64(7), int64(2)}}},
|
|
{Match: "FROM period_daily_records WHERE couple_id = ?", Args: []any{int64(10)}, Columns: []string{"id"}},
|
|
{Match: "FROM period_sex_records WHERE couple_id = ?", Args: []any{int64(10)}, Columns: []string{"id"}},
|
|
})
|
|
periodList(c)
|
|
if out.Code != 200 || !strings.Contains(out.Body.String(), `"user_id":"2"`) {
|
|
t.Fatalf("shared period missing: %d %s", out.Code, out.Body.String())
|
|
}
|
|
}
|