package http import ( "context" "encoding/json" stdhttp "net/http" "testing" "time" "gitea.dcglab.co.uk/steve/restic-manager/internal/api" "gitea.dcglab.co.uk/steve/restic-manager/internal/store" ) func TestSnapshotsFreshnessAndExplicitRefresh(t *testing.T) { t.Parallel() srv, ts, st := rawTestServerWithUI(t) hostID, token := enrolHostForUI(t, srv, st, "snapshot-refresh-host") c := agentDial(t, srv, ts, hostID, token) sendHello(t, c, "snapshot-refresh-host") _ = drainUntil(t, c, api.MsgScheduleSet) cookie := loginAsAdmin(t, st) mutationAt := time.Now().UTC().Add(-time.Minute).Truncate(time.Millisecond) if err := st.CreateJob(context.Background(), store.Job{ ID: "mutation-job", HostID: hostID, Kind: "forget", ActorKind: "user", CreatedAt: mutationAt.Add(-time.Minute), }); err != nil { t.Fatalf("create mutation: %v", err) } if err := st.MarkJobFinished(context.Background(), "mutation-job", "succeeded", 0, nil, "", mutationAt); err != nil { t.Fatalf("finish mutation: %v", err) } get := func() listSnapshotsResponse { req, _ := stdhttp.NewRequest(stdhttp.MethodGet, ts.URL+"/api/hosts/"+hostID+"/snapshots", nil) req.AddCookie(cookie) res, err := stdhttp.DefaultClient.Do(req) if err != nil { t.Fatalf("get snapshots: %v", err) } defer res.Body.Close() var body listSnapshotsResponse if err := json.NewDecoder(res.Body).Decode(&body); err != nil { t.Fatalf("decode snapshots: %v", err) } return body } if body := get(); !body.Stale || body.RefreshedAt != nil { t.Fatalf("unrefreshed projection should be stale: %+v", body) } refreshedAt := mutationAt.Add(time.Second) if err := st.ReplaceHostSnapshots(context.Background(), hostID, nil, refreshedAt); err != nil { t.Fatalf("replace empty: %v", err) } if body := get(); body.Stale || body.RefreshedAt == nil || !body.RefreshedAt.Equal(refreshedAt) { t.Fatalf("fresh empty projection reported incorrectly: %+v", body) } req, _ := stdhttp.NewRequest(stdhttp.MethodPost, ts.URL+"/api/hosts/"+hostID+"/snapshots/refresh", nil) req.AddCookie(cookie) res, err := stdhttp.DefaultClient.Do(req) if err != nil { t.Fatalf("request refresh: %v", err) } defer res.Body.Close() if res.StatusCode != stdhttp.StatusAccepted { t.Fatalf("refresh status = %d, want 202", res.StatusCode) } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() _, raw, err := c.Read(ctx) if err != nil { t.Fatalf("read refresh command: %v", err) } var env api.Envelope if err := json.Unmarshal(raw, &env); err != nil { t.Fatalf("decode envelope: %v", err) } if env.Type != api.MsgSnapshotsRefresh { t.Fatalf("message type = %q, want %q", env.Type, api.MsgSnapshotsRefresh) } }