package main import ( "os" "path/filepath" "runtime" "strings" "testing" "time" ) func testSession(tok string) *sessionSet { s := newSessionStore("").set("ui") s.add(tok, time.Hour) return s } func TestSessionsSurviveARestart(t *testing.T) { path := filepath.Join(t.TempDir(), "sessions.json") a := newSessionStore(path) a.set("ui").add("tok-ui", time.Hour) a.set("chat").add("tok-chat", time.Hour) // "the server restarts": a new store reads the file b := newSessionStore(path) if !b.set("ui").valid("tok-ui") || !b.set("chat").valid("tok-chat") { t.Fatal("sessions must survive a restart") } if b.set("ui").valid("tok-chat") || b.set("np").valid("tok-ui") { t.Fatal("a token only counts for its own kind of session") } } func TestOnlyAHashOfTheTokenIsStoredAndTheFileIsPrivate(t *testing.T) { path := filepath.Join(t.TempDir(), "sessions.json") newSessionStore(path).set("ui").add("super-secret-cookie-value", time.Hour) b, _ := os.ReadFile(path) if strings.Contains(string(b), "super-secret-cookie-value") { t.Fatal("the raw token must never be written") } if runtime.GOOS != "windows" { if fi, _ := os.Stat(path); fi.Mode().Perm() != 0600 { t.Fatalf("mode %v", fi.Mode().Perm()) } } } func TestExpiredSessionsAreIgnoredAndDropped(t *testing.T) { path := filepath.Join(t.TempDir(), "sessions.json") st := newSessionStore(path) st.set("ui").add("old", -time.Minute) st.set("ui").add("fresh", time.Hour) b := newSessionStore(path) if b.set("ui").valid("old") || !b.set("ui").valid("fresh") { t.Fatal("only the unexpired session may load") } } func TestManySessionsAtOnceAndLoggingOutOneKeepsTheRest(t *testing.T) { set := newSessionStore("").set("ui") for _, tok := range []string{"laptop", "phone", "second-window"} { set.add(tok, time.Hour) } for _, tok := range []string{"laptop", "phone", "second-window"} { if !set.valid(tok) { t.Fatalf("%s must be valid at the same time as the others", tok) } } set.remove("phone") if set.valid("phone") || !set.valid("laptop") || !set.valid("second-window") { t.Fatal("removing one must not touch the others") } if set.valid("") || set.valid("never-issued") { t.Fatal("unknown or empty tokens are not sessions") } } func TestSessionRenewalOnlyWhenItIsGettingOld(t *testing.T) { set := newSessionStore("").set("ui") set.add("t", uiSessionTTL) if set.renew("t", uiSessionTTL) { t.Fatal("a brand new session needs no renewal (no disk write on every page load)") } set.add("old", 10*time.Hour) // 14 days minus 6 hours is long past if !set.renew("old", uiSessionTTL) { t.Fatal("an old session must be renewed") } if exp := set.st.sets["ui"][hashToken("old")]; time.Until(exp) < uiSessionTTL-time.Minute { t.Fatal("renewal must give a full lifetime") } if set.renew("unknown", uiSessionTTL) { t.Fatal("an unknown session cannot be renewed") } } func TestANilSetAcceptsNothing(t *testing.T) { var set *sessionSet set.add("x", time.Hour) if set.valid("x") || set.renew("x", time.Hour) { t.Fatal("a missing set must not authorize anything") } }