MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m34s
DEMO_RESET_MINUTES (default 20, 0 = off) resets the database to the sample data. Demo accounts get fixed IDs so sessions survive; requests hold a read lock so the reset never runs under a half-finished request (streams excluded). DEMO_RESET goes to every connection, /auth/status announces the next reset. The app shows "✨ Demo · Reset in MM:SS" in place of the version, then clears cached answers, remounts the views, reloads in the background and shows a short notice; if no event arrives (long polling) it asks the server after the countdown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/sandrodach/mindsift/internal/config"
|
|
"github.com/sandrodach/mindsift/internal/storage"
|
|
"github.com/sandrodach/mindsift/internal/ws"
|
|
)
|
|
|
|
func TestDemoResetKeepsSessionsAndRestoresSampleData(t *testing.T) {
|
|
dbPath := filepath.Join(t.TempDir(), "demo_reset.db")
|
|
store, err := storage.NewStore(dbPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer store.Close()
|
|
if err := store.ResetAndSeedDemo(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
srv := NewServer(&config.Config{DatabasePath: dbPath, JWTSecret: "demo-secret", DemoMode: true}, store, ws.NewHub(), nil)
|
|
router := srv.Router()
|
|
call := func(method, path, token, body string) *httptest.ResponseRecorder {
|
|
req := httptest.NewRequest(method, path, strings.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
if token != "" {
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
}
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
return rec
|
|
}
|
|
var login struct{ Token string }
|
|
_ = json.Unmarshal(call("POST", "/api/v1/auth/login", "", `{"login":"admin","password":"admin"}`).Body.Bytes(), &login)
|
|
countDocs := func() int {
|
|
var docs []json.RawMessage
|
|
_ = json.Unmarshal(call("GET", "/api/v1/documents", login.Token, "").Body.Bytes(), &docs)
|
|
return len(docs)
|
|
}
|
|
seeded := countDocs()
|
|
if seeded == 0 {
|
|
t.Fatal("expected sample notes")
|
|
}
|
|
if rec := call("POST", "/api/v1/documents", login.Token, `{"title":"Testnotiz"}`); rec.Code >= 300 {
|
|
t.Fatalf("create note: %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
|
|
// Requests running during the reset must neither fail half-way nor see missing tables
|
|
var wg sync.WaitGroup
|
|
failures := make(chan int, 50)
|
|
for i := 0; i < 50; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
if rec := call("GET", "/api/v1/documents", login.Token, ""); rec.Code != http.StatusOK {
|
|
failures <- rec.Code
|
|
}
|
|
}()
|
|
}
|
|
srv.resetDemo()
|
|
wg.Wait()
|
|
close(failures)
|
|
for code := range failures {
|
|
t.Errorf("request during reset failed with %d", code)
|
|
}
|
|
|
|
if rec := call("GET", "/api/v1/auth/me", login.Token, ""); rec.Code != http.StatusOK {
|
|
t.Fatalf("session must survive the reset, got %d", rec.Code)
|
|
}
|
|
if n := countDocs(); n != seeded {
|
|
t.Errorf("reset should restore the %d sample notes, got %d", seeded, n)
|
|
}
|
|
|
|
srv.setDemoNextReset(time.Now().Add(90 * time.Second))
|
|
var st struct {
|
|
DemoNextReset string `json:"demoNextReset"`
|
|
DemoResetInSeconds int `json:"demoResetInSeconds"`
|
|
}
|
|
_ = json.Unmarshal(call("GET", "/api/v1/auth/status", "", "").Body.Bytes(), &st)
|
|
if st.DemoNextReset == "" || st.DemoResetInSeconds < 85 || st.DemoResetInSeconds > 90 {
|
|
t.Errorf("status should announce the next reset: %+v", st)
|
|
}
|
|
}
|