From 3b776621b1fc0b6d9f765dc4eec49861dffd2fed Mon Sep 17 00:00:00 2001 From: Nightshift Date: Sun, 19 Apr 2026 08:00:50 +0000 Subject: [PATCH] [nightshift] api-contract-verify: fix GUI API contract issues --- internal/gui/server.go | 13 ++++++++++--- internal/gui/server_test.go | 5 +++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/gui/server.go b/internal/gui/server.go index 0a11573..baf2848 100644 --- a/internal/gui/server.go +++ b/internal/gui/server.go @@ -155,6 +155,10 @@ func (s *Server) enroll(w http.ResponseWriter, r *http.Request) { writeJSONCode(w, http.StatusBadRequest, map[string]any{"ok": false, "error": "customDays must be non-negative"}) return } + if req.Mode == string(model.LeaseModeTimed) && req.CustomDays == 0 && req.Days == 0 { + writeJSONCode(w, http.StatusBadRequest, map[string]any{"ok": false, "error": "timed mode requires days > 0 or customDays > 0"}) + return + } password := strings.TrimSpace(req.Password) if password == "" { password = strings.TrimSpace(os.Getenv("TAILSTICK_OPERATOR_PASSWORD")) @@ -174,13 +178,16 @@ func (s *Server) enroll(w http.ResponseWriter, r *http.Request) { writeJSONCode(w, http.StatusBadRequest, map[string]any{"ok": false, "error": err.Error()}) return } - writeJSON(w, map[string]any{ + resp := map[string]any{ "ok": true, "leaseId": rec.LeaseID, "deviceName": rec.DeviceName, "mode": rec.Mode, - "expiresAt": rec.ExpiresAt, - }) + } + if rec.ExpiresAt != nil { + resp["expiresAt"] = rec.ExpiresAt + } + writeJSON(w, resp) } func (s *Server) index(w http.ResponseWriter, r *http.Request) { diff --git a/internal/gui/server_test.go b/internal/gui/server_test.go index 8821403..a6e597f 100644 --- a/internal/gui/server_test.go +++ b/internal/gui/server_test.go @@ -112,6 +112,11 @@ func TestEnrollRejectsInvalidModeAndNegativeDurations(t *testing.T) { body: `{"mode":"timed","channel":"stable","customDays":-1}`, want: `customDays must be non-negative`, }, + { + name: "timed mode with zero days", + body: `{"mode":"timed","channel":"stable","days":0}`, + want: `timed mode requires days > 0`, + }, } { t.Run(tc.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/enroll", bytes.NewBufferString(tc.body))