package auth import ( "net/http" "net/http/httptest" "os" "path/filepath" "testing" "time" ) func TestKeyStoreReloadIfChanged(t *testing.T) { path := filepath.Join(t.TempDir(), "apikeys.json") // Writer process mints into the store. writer, err := OpenKeyStore(path) if err != nil { t.Fatal(err) } key, rec, err := writer.Mint("integration", []string{"mcp"}) if err != nil { t.Fatal(err) } // Reader process opens the same file fresh. reader, err := OpenKeyStore(path) if err != nil { t.Fatal(err) } req := apiKeyReq(key) if _, err := (APIKeyAuth{Keys: reader}).Authenticate(req); err != nil { t.Fatalf("fresh key should validate: %v", err) } // Writer mints a second key AFTER the reader loaded → reader must reload. time.Sleep(10 * time.Millisecond) // ensure mtime advances key2, _, err := writer.Mint("companion", []string{"write"}) if err != nil { t.Fatal(err) } if _, err := (APIKeyAuth{Keys: reader}).Authenticate(apiKeyReq(key2)); err == nil { t.Fatal("key minted after load should NOT validate before reload") } if err := reader.ReloadIfChanged(); err != nil { t.Fatal(err) } if _, err := (APIKeyAuth{Keys: reader}).Authenticate(apiKeyReq(key2)); err != nil { t.Fatalf("key should validate after ReloadIfChanged: %v", err) } // Unchanged file → no-op (and keys survive). if err := reader.ReloadIfChanged(); err != nil { t.Fatal(err) } if _, err := (APIKeyAuth{Keys: reader}).Authenticate(apiKeyReq(key)); err != nil { t.Fatalf("key should still validate: %v", err) } // Revoke on the writer side → reader sees it after reload. if err := writer.Revoke(rec.ID); err != nil { t.Fatal(err) } if err := reader.ReloadIfChanged(); err != nil { t.Fatal(err) } if _, err := (APIKeyAuth{Keys: reader}).Authenticate(apiKeyReq(key)); err == nil { t.Fatal("revoked key should not validate") } } func apiKeyReq(key string) *http.Request { r := httptest.NewRequest(http.MethodGet, "/api/nodes", nil) r.Header.Set("X-API-Key", key) return r } func TestKeyStoreReloadCorruptFile(t *testing.T) { path := filepath.Join(t.TempDir(), "apikeys.json") ks, err := OpenKeyStore(path) if err != nil { t.Fatal(err) } raw := []byte(`{not json`) if err := os.WriteFile(path, raw, 0o600); err != nil { t.Fatal(err) } time.Sleep(10 * time.Millisecond) if err := ks.ReloadIfChanged(); err == nil { t.Fatal("corrupt file should error") } }