/
t3
/
iam-server
Обзор
Документация
Войти
/
t3
/
iam-server
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
test/user_handler_test.go
491 строка
13 KB
Ivan Shibkikh
added more unit tests
19 июл 2026, 18:15
19 июл 2026, 18:15
ceafa88
Код
Авторство
О чём код?
package test import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" "gitverse.ru/t3/iam-server/internal/api" "gitverse.ru/t3/iam-server/internal/service" "gitverse.ru/t3/iam-server/internal/storage" ) // setupUserHandler создает все зависимости для тестирования UserHandler func setupUserHandler(t *testing.T) (*api.UserHandler, *storage.UserRepo, *storage.Storage) { t.Helper() ctx := context.Background() s, err := storage.New(":memory:") if err != nil { t.Fatalf("failed to create storage: %v", err) } if err := s.Init(ctx); err != nil { t.Fatalf("failed to init storage: %v", err) } userRepo := storage.NewUserRepo(s) roleRepo := storage.NewRoleRepo(s) permRepo := storage.NewPermissionRepo(s) accessKeyRepo := storage.NewAccessKeyRepo(s) auditRepo := storage.NewAuditRepo(s) auditSvc := service.NewAuditService(auditRepo, nil) masterKeyHex := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" cryptoSvc, err := service.NewCryptoService(masterKeyHex) if err != nil { t.Fatalf("NewCryptoService: %v", err) } handler := api.NewUserHandler(userRepo, permRepo, accessKeyRepo, cryptoSvc, auditSvc) _ = roleRepo return handler, userRepo, s } func createTestUser(t *testing.T, handler *api.UserHandler) api.CreateUserResponse { t.Helper() body := `{"display_name":"Test User"}` req := httptest.NewRequest("POST", "/api/v1/users", strings.NewReader(body)) req = withActorService(req, "admin", "test-admin:abc", "127.0.0.1", []string{"*"}, "req-x") req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() handler.Create(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("createTestUser: expected 201, got %d: %s", rec.Code, rec.Body.String()) } var resp api.CreateUserResponse if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { t.Fatalf("createTestUser: parse: %v", err) } return resp } func withActor(r *http.Request) *http.Request { actor := api.Actor{ Type: "admin", ID: "test-admin:abc123", IP: "127.0.0.1", Scopes: []string{"*"}, } ctx := api.WithActor(r.Context(), actor) ctx = api.WithRequestID(ctx, "test-req") return r.WithContext(ctx) } func withActorService(r *http.Request, actorType, actorID, ip string, scopes []string, reqID string) *http.Request { actor := api.Actor{ Type: actorType, ID: actorID, IP: ip, Scopes: scopes, } ctx := api.WithActor(r.Context(), actor) ctx = api.WithRequestID(ctx, reqID) return r.WithContext(ctx) } func TestUserHandler_Create(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) if resp.ID == "" { t.Fatal("expected non-empty user ID") } if resp.DisplayName != "Test User" { t.Fatalf("expected display name 'Test User', got %q", resp.DisplayName) } if resp.AccessKey == "" { t.Fatal("expected non-empty access key") } if resp.AccessSecret == "" { t.Fatal("expected non-empty secret key") } if !strings.HasPrefix(resp.AccessKey, "AKIA") { t.Fatalf("expected AKIA prefix, got %q", resp.AccessKey) } if len(resp.AccessSecret) != 40 { t.Fatalf("expected 40-char secret, got %d", len(resp.AccessSecret)) } } func TestUserHandler_Create_InvalidJSON(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() req := httptest.NewRequest("POST", "/api/v1/users", strings.NewReader("not-json")) req = withActor(req) rec := httptest.NewRecorder() handler.Create(rec, req) if rec.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d", rec.Code) } } func TestUserHandler_Create_DisplayNameTooLong(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() body := `{"display_name":"` + strings.Repeat("x", 300) + `"}` req := httptest.NewRequest("POST", "/api/v1/users", strings.NewReader(body)) req = withActor(req) rec := httptest.NewRecorder() handler.Create(rec, req) if rec.Code != http.StatusBadRequest { t.Fatalf("expected 400 for long display_name, got %d", rec.Code) } } func TestUserHandler_List(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() // Create a user via handler createTestUser(t, handler) req := httptest.NewRequest("GET", "/api/v1/users", nil) req = withActor(req) rec := httptest.NewRecorder() handler.List(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d", rec.Code) } var users []api.UserResponse if err := json.Unmarshal(rec.Body.Bytes(), &users); err != nil { t.Fatalf("failed to parse: %v", err) } if len(users) != 1 { t.Fatalf("expected 1 user, got %d", len(users)) } if users[0].DisplayName != "Test User" { t.Fatalf("expected 'Test User', got %q", users[0].DisplayName) } } func TestUserHandler_Update(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) body := `{"display_name":"Renamed"}` req := httptest.NewRequest("PATCH", "/api/v1/users/"+resp.ID, strings.NewReader(body)) req = withActor(req) req.SetPathValue("id", resp.ID) rec := httptest.NewRecorder() handler.Update(rec, req) if rec.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d: %s", rec.Code, rec.Body.String()) } } func TestUserHandler_Update_NotFound(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() body := `{"display_name":"Whatever"}` req := httptest.NewRequest("PATCH", "/api/v1/users/nonexistent", strings.NewReader(body)) req = withActor(req) req.SetPathValue("id", "nonexistent") rec := httptest.NewRecorder() handler.Update(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", rec.Code) } } func TestUserHandler_Delete(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) req := httptest.NewRequest("DELETE", "/api/v1/users/"+resp.ID, nil) req = withActor(req) req.SetPathValue("id", resp.ID) rec := httptest.NewRecorder() handler.Delete(rec, req) if rec.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d: %s", rec.Code, rec.Body.String()) } } func TestUserHandler_Delete_NotFound(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() req := httptest.NewRequest("DELETE", "/api/v1/users/nonexistent", nil) req = withActor(req) req.SetPathValue("id", "nonexistent") rec := httptest.NewRecorder() handler.Delete(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", rec.Code) } } func TestUserHandler_CreateAccessKey(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) body := `{}` req := httptest.NewRequest("POST", "/api/v1/users/"+resp.ID+"/access-keys", strings.NewReader(body)) req = withActor(req) req.SetPathValue("id", resp.ID) rec := httptest.NewRecorder() handler.CreateAccessKey(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("expected 201, got %d: %s", rec.Code, rec.Body.String()) } } func TestUserHandler_CreateAccessKey_MaxLimit(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) // User already has 1 key from createTestUser, create 1 more to reach max req2 := httptest.NewRequest("POST", "/api/v1/users/"+resp.ID+"/access-keys", strings.NewReader("{}")) req2 = withActor(req2) req2.SetPathValue("id", resp.ID) rec2 := httptest.NewRecorder() handler.CreateAccessKey(rec2, req2) if rec2.Code != http.StatusCreated { t.Fatalf("second key: expected 201, got %d: %s", rec2.Code, rec2.Body.String()) } // Third key should fail (max is 2) req3 := httptest.NewRequest("POST", "/api/v1/users/"+resp.ID+"/access-keys", strings.NewReader("{}")) req3 = withActor(req3) req3.SetPathValue("id", resp.ID) rec3 := httptest.NewRecorder() handler.CreateAccessKey(rec3, req3) if rec3.Code != http.StatusConflict { t.Fatalf("expected 409 for max keys, got %d: %s", rec3.Code, rec3.Body.String()) } } func TestUserHandler_CreateAccessKey_WithExpiry(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) future := time.Now().UTC().Add(24 * time.Hour).Format(time.RFC3339) body := `{"expires_at":"` + future + `"}` req := httptest.NewRequest("POST", "/api/v1/users/"+resp.ID+"/access-keys", strings.NewReader(body)) req = withActor(req) req.SetPathValue("id", resp.ID) rec := httptest.NewRecorder() handler.CreateAccessKey(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("expected 201, got %d: %s", rec.Code, rec.Body.String()) } } func TestUserHandler_ListAccessKeys(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) // createTestUser already creates 1 key // Create one more key req2 := httptest.NewRequest("POST", "/api/v1/users/"+resp.ID+"/access-keys", strings.NewReader("{}")) req2 = withActor(req2) req2.SetPathValue("id", resp.ID) rec2 := httptest.NewRecorder() handler.CreateAccessKey(rec2, req2) req := httptest.NewRequest("GET", "/api/v1/users/"+resp.ID+"/access-keys", nil) req = withActor(req) req.SetPathValue("id", resp.ID) rec := httptest.NewRecorder() handler.ListAccessKeys(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } var keys []api.AccessKeyResponse if err := json.Unmarshal(rec.Body.Bytes(), &keys); err != nil { t.Fatalf("parse: %v", err) } if len(keys) != 2 { t.Fatalf("expected 2 keys, got %d", len(keys)) } } func TestUserHandler_UpdateAccessKey_Deactivate(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) // createTestUser creates 1 key with access key in response // List keys to get key ID reqList := httptest.NewRequest("GET", "/api/v1/users/"+resp.ID+"/access-keys", nil) reqList = withActor(reqList) reqList.SetPathValue("id", resp.ID) recList := httptest.NewRecorder() handler.ListAccessKeys(recList, reqList) var keys []api.AccessKeyResponse json.Unmarshal(recList.Body.Bytes(), &keys) if len(keys) == 0 { t.Fatal("expected at least 1 key to deactivate") } keyID := keys[0].ID body := `{"status":"Inactive"}` req := httptest.NewRequest("PATCH", "/api/v1/users/"+resp.ID+"/access-keys/"+keyID, strings.NewReader(body)) req = withActor(req) req.SetPathValue("id", resp.ID) req.SetPathValue("keyId", keyID) rec := httptest.NewRecorder() handler.UpdateAccessKey(rec, req) if rec.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d: %s", rec.Code, rec.Body.String()) } } func TestUserHandler_UpdateAccessKey_InvalidStatus(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) reqList := httptest.NewRequest("GET", "/api/v1/users/"+resp.ID+"/access-keys", nil) reqList = withActor(reqList) reqList.SetPathValue("id", resp.ID) recList := httptest.NewRecorder() handler.ListAccessKeys(recList, reqList) var keys []api.AccessKeyResponse json.Unmarshal(recList.Body.Bytes(), &keys) if len(keys) == 0 { t.Fatal("expected at least 1 key") } body := `{"status":"Deleted"}` req := httptest.NewRequest("PATCH", "/api/v1/users/"+resp.ID+"/access-keys/"+keys[0].ID, strings.NewReader(body)) req = withActor(req) req.SetPathValue("id", resp.ID) req.SetPathValue("keyId", keys[0].ID) rec := httptest.NewRecorder() handler.UpdateAccessKey(rec, req) if rec.Code != http.StatusBadRequest { t.Fatalf("expected 400 for invalid status, got %d", rec.Code) } } func TestUserHandler_DeleteAccessKey(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) reqList := httptest.NewRequest("GET", "/api/v1/users/"+resp.ID+"/access-keys", nil) reqList = withActor(reqList) reqList.SetPathValue("id", resp.ID) recList := httptest.NewRecorder() handler.ListAccessKeys(recList, reqList) var keys []api.AccessKeyResponse json.Unmarshal(recList.Body.Bytes(), &keys) if len(keys) == 0 { t.Fatal("expected at least 1 key") } req := httptest.NewRequest("DELETE", "/api/v1/users/"+resp.ID+"/access-keys/"+keys[0].ID, nil) req = withActor(req) req.SetPathValue("id", resp.ID) req.SetPathValue("keyId", keys[0].ID) rec := httptest.NewRecorder() handler.DeleteAccessKey(rec, req) if rec.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d: %s", rec.Code, rec.Body.String()) } } func TestUserHandler_Inspect(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() resp := createTestUser(t, handler) req := httptest.NewRequest("GET", "/api/v1/users/"+resp.ID, nil) req = withActor(req) req.SetPathValue("id", resp.ID) rec := httptest.NewRecorder() handler.Inspect(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } var insp api.InspectUserResponse if err := json.Unmarshal(rec.Body.Bytes(), &insp); err != nil { t.Fatalf("parse: %v", err) } if insp.ID != resp.ID { t.Fatalf("ID mismatch") } if len(insp.Keys) != 1 { t.Fatalf("expected 1 key, got %d", len(insp.Keys)) } } func TestUserHandler_Inspect_NotFound(t *testing.T) { handler, _, s := setupUserHandler(t) defer s.Close() req := httptest.NewRequest("GET", "/api/v1/users/nonexistent", nil) req = withActor(req) req.SetPathValue("id", "nonexistent") rec := httptest.NewRecorder() handler.Inspect(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", rec.Code) } }