package mock import ( "context" "errors" "tera-clone/services/tera-api/internal/domain" "tera-clone/services/tera-api/internal/systemtime" "testing" "time" ) type loginTimeController struct { now *time.Time calls int fail bool } func (c *loginTimeController) Sync(_ context.Context, r systemtime.Request) (systemtime.Result, error) { c.calls++ if c.fail { return systemtime.Result{}, errors.New("unavailable") } *c.now = r.BrowserTime return systemtime.Result{AppliedTime: r.BrowserTime, Timezone: r.Timezone}, nil } func TestFirstAuthorizedLoginSyncsClockBeforeIssuingSession(t *testing.T) { now := time.Unix(300, 0).UTC() controller := &loginTimeController{now: &now} s, err := NewStoreWithOptions(func() time.Time { return now }, StoreOptions{TimeController: controller}) if err != nil { t.Fatal(err) } defer s.Close() target := time.Date(2026, 9, 8, 3, 0, 0, 0, time.UTC) request := domain.AuthLoginRequest{Username: "admin", Password: "wrong", BrowserTime: target} if _, err := s.Login(context.Background(), request); err == nil || controller.calls != 0 { t.Fatal("invalid credentials changed clock") } if _, err := s.Login(context.Background(), domain.AuthLoginRequest{Username: "port-operator", Password: "mock-port-operator-secret", BrowserTime: target}); err != nil || controller.calls != 0 { t.Fatal("port operator changed clock") } request.Password = "mock-admin-secret" session, err := s.Login(context.Background(), request) if err != nil { t.Fatal(err) } if controller.calls != 1 || !now.Equal(target) || !session.ExpiresAt.Equal(target.Add(8*time.Hour)) { t.Fatalf("clock/session mismatch: calls=%d expires=%v", controller.calls, session.ExpiresAt) } if _, err = s.ActorBySession(context.Background(), session.SessionToken); err != nil { t.Fatalf("session invalid after clock jump: %v", err) } request.BrowserTime = target.Add(time.Hour) if _, err = s.Login(context.Background(), request); err != nil { t.Fatal(err) } if controller.calls != 1 { t.Fatal("subsequent login changed clock again") } } func TestAutomaticLoginTimeSyncFailureRetriesWithoutBlockingLogin(t *testing.T) { now := fixedClock() controller := &loginTimeController{now: &now, fail: true} s, err := NewStoreWithOptions(func() time.Time { return now }, StoreOptions{TimeController: controller}) if err != nil { t.Fatal(err) } defer s.Close() request := domain.AuthLoginRequest{Username: "admin", Password: "mock-admin-secret", BrowserTime: now.Add(time.Hour)} if _, err = s.Login(context.Background(), request); err != nil { t.Fatal(err) } if s.browserTimeSynced { t.Fatal("failed sync marked successful") } controller.fail = false if _, err = s.Login(context.Background(), request); err != nil { t.Fatal(err) } if controller.calls != 2 || !s.browserTimeSynced { t.Fatal("failed sync not retried") } }