package graphql_test import ( "context" "fmt" "os" "tercul/internal/adapters/graphql" "tercul/internal/adapters/graphql/model" "tercul/internal/app/auth" "tercul/internal/domain" platform_auth "tercul/internal/platform/auth" "tercul/internal/testutil" "testing" "github.com/stretchr/testify/suite" ) type WorkResolversTestSuite struct { testutil.IntegrationTestSuite queryResolver graphql.QueryResolver mutationResolver graphql.MutationResolver } func TestWorkResolvers(t *testing.T) { suite.Run(t, new(WorkResolversTestSuite)) } func (s *WorkResolversTestSuite) SetupSuite() { s.IntegrationTestSuite.SetupSuite(&testutil.TestConfig{ DBPath: "work_resolvers_test.db", }) } func (s *WorkResolversTestSuite) TearDownSuite() { s.IntegrationTestSuite.TearDownSuite() _ = os.Remove("work_resolvers_test.db") } func (s *WorkResolversTestSuite) SetupTest() { s.IntegrationTestSuite.SetupTest() resolver := &graphql.Resolver{App: s.App} s.queryResolver = resolver.Query() s.mutationResolver = resolver.Mutation() } // Helper to create a user for tests func (s *WorkResolversTestSuite) createUser(username, email, password string, role domain.UserRole) *domain.User { resp, err := s.App.Auth.Commands.Register(context.Background(), auth.RegisterInput{ Username: username, Email: email, Password: password, }) s.Require().NoError(err) user, err := s.App.User.Queries.User(context.Background(), resp.User.ID) s.Require().NoError(err) if role != user.Role { user.Role = role err = s.DB.Save(user).Error s.Require().NoError(err) } return user } // Helper to create a context with JWT claims func (s *WorkResolversTestSuite) contextWithClaims(user *domain.User) context.Context { return testutil.ContextWithClaims(context.Background(), &platform_auth.Claims{ UserID: user.ID, Role: string(user.Role), }) } func (s *WorkResolversTestSuite) TestCreateWork() { user := s.createUser("work-creator", "work-creator@test.com", "password", domain.UserRoleContributor) ctx := s.contextWithClaims(user) s.Run("Success", func() { // Arrange input := model.WorkInput{ Name: "My First Work", Language: "en", } // Act work, err := s.mutationResolver.CreateWork(ctx, input) // Assert s.Require().NoError(err) s.Require().NotNil(work) s.Equal("My First Work", work.Name) s.Equal("en", work.Language) // Verify in DB dbWork, err := s.App.Work.Queries.GetWorkByID(context.Background(), 1) s.Require().NoError(err) s.Require().NotNil(dbWork) s.Equal("My First Work", dbWork.Title) }) } func (s *WorkResolversTestSuite) TestWorkQuery() { // Arrange user := s.createUser("work-reader", "work-reader@test.com", "password", domain.UserRoleReader) ctx := s.contextWithClaims(user) // Create a work to query domainWork := &domain.Work{Title: "Query Me", TranslatableModel: domain.TranslatableModel{Language: "es"}} createdWork, err := s.App.Work.Commands.CreateWork(ctx, domainWork) s.Require().NoError(err) workID := fmt.Sprintf("%d", createdWork.ID) s.Run("Success", func() { // Act work, err := s.queryResolver.Work(ctx, workID) // Assert s.Require().NoError(err) s.Require().NotNil(work) s.Equal("Query Me", work.Name) s.Equal("es", work.Language) }) s.Run("Not Found", func() { // Act work, err := s.queryResolver.Work(ctx, "99999") // Assert s.Require().NoError(err) s.Require().Nil(work) }) } func (s *WorkResolversTestSuite) TestUpdateWork() { // Arrange user := s.createUser("work-updater", "work-updater@test.com", "password", domain.UserRoleContributor) admin := s.createUser("work-admin", "work-admin@test.com", "password", domain.UserRoleAdmin) otherUser := s.createUser("other-user", "other-user@test.com", "password", domain.UserRoleContributor) // Create a work to update domainWork := &domain.Work{Title: "Update Me", TranslatableModel: domain.TranslatableModel{Language: "fr"}} createdWork, err := s.App.Work.Commands.CreateWork(s.contextWithClaims(user), domainWork) s.Require().NoError(err) workID := fmt.Sprintf("%d", createdWork.ID) s.Run("Success as owner", func() { // Arrange ctx := s.contextWithClaims(user) input := model.WorkInput{Name: "Updated Title", Language: "fr"} // Act work, err := s.mutationResolver.UpdateWork(ctx, workID, input) // Assert s.Require().NoError(err) s.Equal("Updated Title", work.Name) }) s.Run("Success as admin", func() { // Arrange ctx := s.contextWithClaims(admin) input := model.WorkInput{Name: "Updated by Admin", Language: "fr"} // Act work, err := s.mutationResolver.UpdateWork(ctx, workID, input) // Assert s.Require().NoError(err) s.Equal("Updated by Admin", work.Name) }) s.Run("Forbidden for other user", func() { // Arrange ctx := s.contextWithClaims(otherUser) input := model.WorkInput{Name: "Should Not Update", Language: "fr"} // Act _, err := s.mutationResolver.UpdateWork(ctx, workID, input) // Assert s.Require().Error(err) s.ErrorIs(err, domain.ErrForbidden) }) } func (s *WorkResolversTestSuite) TestDeleteWork() { // Arrange user := s.createUser("work-deletor", "work-deletor@test.com", "password", domain.UserRoleContributor) admin := s.createUser("work-admin-deletor", "work-admin-deletor@test.com", "password", domain.UserRoleAdmin) otherUser := s.createUser("other-user-deletor", "other-user-deletor@test.com", "password", domain.UserRoleContributor) s.Run("Success as owner", func() { // Arrange domainWork := &domain.Work{Title: "Delete Me", TranslatableModel: domain.TranslatableModel{Language: "de"}} createdWork, err := s.App.Work.Commands.CreateWork(s.contextWithClaims(user), domainWork) s.Require().NoError(err) workID := fmt.Sprintf("%d", createdWork.ID) ctx := s.contextWithClaims(user) // Act ok, err := s.mutationResolver.DeleteWork(ctx, workID) // Assert s.Require().NoError(err) s.True(ok) }) s.Run("Success as admin", func() { // Arrange domainWork := &domain.Work{Title: "Delete Me Admin", TranslatableModel: domain.TranslatableModel{Language: "de"}} createdWork, err := s.App.Work.Commands.CreateWork(s.contextWithClaims(user), domainWork) s.Require().NoError(err) workID := fmt.Sprintf("%d", createdWork.ID) ctx := s.contextWithClaims(admin) // Act ok, err := s.mutationResolver.DeleteWork(ctx, workID) // Assert s.Require().NoError(err) s.True(ok) }) s.Run("Forbidden for other user", func() { // Arrange domainWork := &domain.Work{Title: "Don't Delete Me", TranslatableModel: domain.TranslatableModel{Language: "de"}} createdWork, err := s.App.Work.Commands.CreateWork(s.contextWithClaims(user), domainWork) s.Require().NoError(err) workID := fmt.Sprintf("%d", createdWork.ID) ctx := s.contextWithClaims(otherUser) // Act _, err = s.mutationResolver.DeleteWork(ctx, workID) // Assert s.Require().Error(err) s.ErrorIs(err, domain.ErrForbidden) }) } func (s *WorkResolversTestSuite) TestWorksQuery() { // Arrange user := s.createUser("works-reader", "works-reader@test.com", "password", domain.UserRoleReader) ctx := s.contextWithClaims(user) // Create some works _, err := s.App.Work.Commands.CreateWork(ctx, &domain.Work{Title: "Work 1", TranslatableModel: domain.TranslatableModel{Language: "en"}}) s.Require().NoError(err) _, err = s.App.Work.Commands.CreateWork(ctx, &domain.Work{Title: "Work 2", TranslatableModel: domain.TranslatableModel{Language: "en"}}) s.Require().NoError(err) s.Run("Success", func() { // Act works, err := s.queryResolver.Works(ctx, nil, nil, nil, nil, nil, nil, nil) // Assert s.Require().NoError(err) s.True(len(works) >= 2) // >= because other tests might have created works }) }