Verified Commit c292954a authored by BoxBoxJason's avatar BoxBoxJason Committed by GitLab
Browse files

feat: support achievements CRUD operations

Changelog: Improvements
parent 1b0ad7be
Loading
Loading
Loading
Loading

achievements.go

0 → 100644
+1127 −0

File added.

Preview size limit exceeded, changes collapsed.

achievements_test.go

0 → 100644
+1470 −0

File added.

Preview size limit exceeded, changes collapsed.

+2 −0
Original line number Diff line number Diff line
@@ -165,6 +165,7 @@ type Client struct {

	// Services used for talking to different parts of the GitLab API.
	AccessRequests                      AccessRequestsServiceInterface
	Achievements                        AchievementsServiceInterface
	AdminCompliancePolicySettings       AdminCompliancePolicySettingsServiceInterface
	AlertManagement                     AlertManagementServiceInterface
	Appearance                          AppearanceServiceInterface
@@ -494,6 +495,7 @@ func NewAuthSourceClient(as AuthSource, options ...ClientOptionFunc) (*Client, e

	// Create all the public services.
	c.AccessRequests = &AccessRequestsService{client: c}
	c.Achievements = &AchievementsService{client: c}
	c.AdminCompliancePolicySettings = &AdminCompliancePolicySettingsService{client: c}
	c.AlertManagement = &AlertManagementService{client: c}
	c.Appearance = &AppearanceService{client: c}
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ package gitlab

var serviceMap = map[any]any{
	&AccessRequestsService{}:                      (*AccessRequestsServiceInterface)(nil),
	&AchievementsService{}:                        (*AchievementsServiceInterface)(nil),
	&AdminCompliancePolicySettingsService{}:       (*AdminCompliancePolicySettingsServiceInterface)(nil),
	&AlertManagementService{}:                     (*AlertManagementServiceInterface)(nil),
	&AppearanceService{}:                          (*AppearanceServiceInterface)(nil),
+182 −0
Original line number Diff line number Diff line
//go:build integration

package gitlab_test

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	gitlab "gitlab.com/gitlab-org/api/client-go/v2"
)

func TestAchievementLifeCycle(t *testing.T) {
	t.Parallel()

	client := SetupIntegrationClient(t)

	// GIVEN a test group (namespace) and a test user
	group := CreateTestGroup(t, client)

	user, err := CreateTestUser(t, client)
	require.NoError(t, err, "CreateTestUser failed")

	// STEP 1: Create an achievement
	// WHEN creating a new achievement in the group's namespace
	createOpt := gitlab.CreateAchievementOptions{
		Name:        gitlab.Ptr("Integration Test Achievement"),
		Description: gitlab.Ptr("Initial description"),
	}

	achievement, _, err := client.Achievements.CreateAchievement(group.ID, &createOpt)
	require.NoError(t, err, "CreateAchievement failed")
	require.NotNil(t, achievement)

	// clean up in case test fails too early
	t.Cleanup(func() {
		_, _, err := client.Achievements.DeleteAchievement(achievement.ID, gitlab.WithContext(context.Background()))
		if err != nil {
			t.Logf("failed to delete test achievement in cleanup: %v", err)
		}
	})

	// THEN the achievement should have the provided fields set correctly
	assert.Equal(t, "Integration Test Achievement", achievement.Name, "Field: Name")
	assert.Equal(t, "Initial description", deref(t, achievement.Description), "Field: Description")
	assert.Equal(t, group.ID, achievement.NamespaceID, "Field: NamespaceID")

	// STEP 2: Update the achievement
	// WHEN updating the achievement with new values
	updateOpt := gitlab.UpdateAchievementOptions{
		Name:        gitlab.Ptr("Updated Achievement Title"),
		Description: gitlab.Ptr("Updated description"),
	}

	updatedAchievement, _, err := client.Achievements.UpdateAchievement(achievement.ID, &updateOpt)
	require.NoError(t, err, "UpdateAchievement failed")
	require.NotNil(t, updatedAchievement)

	// THEN the achievement should have the updated fields set correctly
	assert.Equal(t, "Updated Achievement Title", updatedAchievement.Name, "Field: Name")
	assert.Equal(t, "Updated description", deref(t, updatedAchievement.Description), "Field: Description")

	// STEP 3: Award the achievement to the test user
	// WHEN awarding the achievement to a user
	awardOpt := gitlab.AwardAchievementOptions{
		AwardMessage: gitlab.Ptr("Congrats!"),
	}

	userAchievement, _, err := client.Achievements.AwardAchievement(achievement.ID, user.ID, &awardOpt)
	require.NoError(t, err, "AwardAchievement failed")
	require.NotNil(t, userAchievement)

	// clean up in case test fails too early
	t.Cleanup(func() {
		_, _, err := client.Achievements.RevokeAchievement(userAchievement.ID, gitlab.WithContext(context.Background()))
		if err != nil {
			t.Logf("failed to revoke test user achievement in cleanup: %v", err)
		}
	})

	// THEN the user achievement should reflect the award
	assert.Equal(t, achievement.ID, userAchievement.AchievementID, "Field: AchievementID")
	assert.Equal(t, user.ID, userAchievement.UserID, "Field: UserID")
	assert.Equal(t, "Congrats!", deref(t, userAchievement.AwardMessage), "Field: AwardMessage")
	assert.Nil(t, userAchievement.RevokedAt, "Field: RevokedAt")

	// STEP 4: List the user's achievements
	// WHEN listing achievements awarded to the user
	// (newly awarded achievements are hidden from the profile by default,
	// unless the instance setting auto_accept_awarded_achievements is
	// enabled; IncludeHidden is required here since we're listing as the
	// group owner/awarder rather than the recipient)
	userAchievements, _, err := client.Achievements.ListUserAchievements(user.Username, &gitlab.ListUserAchievementsOptions{
		IncludeHidden: gitlab.Ptr(true),
	})
	require.NoError(t, err, "ListUserAchievements failed")

	// THEN the awarded achievement should be present in the list
	require.Len(t, userAchievements, 1)
	assert.Equal(t, userAchievement.ID, userAchievements[0].ID, "Field: ID")
	assert.Equal(t, achievement.ID, userAchievements[0].AchievementID, "Field: AchievementID")

	// STEP 4b: List the achievements defined in the namespace
	// WHEN listing achievements for the group, filtered to the one we created
	achievements, _, err := client.Achievements.ListAchievements(group.FullPath, &gitlab.ListAchievementsOptions{
		IDs: []int64{achievement.ID},
	})
	require.NoError(t, err, "ListAchievements failed")

	// THEN the created achievement should be present in the list
	require.Len(t, achievements, 1)
	assert.Equal(t, achievement.ID, achievements[0].ID, "Field: ID")
	assert.Equal(t, "Updated Achievement Title", achievements[0].Name, "Field: Name")

	// STEP 4c: List the achievement's recipients
	// WHEN listing the users the achievement has been awarded to
	recipients, _, err := client.Achievements.ListAchievementRecipients(group.FullPath, achievement.ID, nil)
	require.NoError(t, err, "ListAchievementRecipients failed")

	// THEN the awarded user should be present in the list
	require.Len(t, recipients, 1)
	assert.Equal(t, userAchievement.ID, recipients[0].ID, "Field: ID")
	assert.Equal(t, user.ID, recipients[0].UserID, "Field: UserID")

	// STEP 4d: List the achievement's unique recipients
	// WHEN listing the distinct users who have received the achievement
	uniqueUsers, _, err := client.Achievements.ListAchievementUniqueUsers(group.FullPath, achievement.ID, nil)
	require.NoError(t, err, "ListAchievementUniqueUsers failed")

	// THEN the awarded user should be present in the list
	require.Len(t, uniqueUsers, 1)
	assert.Equal(t, user.ID, uniqueUsers[0].ID, "Field: ID")
	assert.Equal(t, user.Username, uniqueUsers[0].Username, "Field: Username")

	// STEP 5: Update the user achievement's showOnProfile flag
	// WHEN toggling showOnProfile to false as the achievement recipient
	// (only the recipient themselves may update this field, not the awarding admin)
	asRecipient := ImpersonateTestUser(t, client, user.ID)
	updatedUserAchievement, _, err := asRecipient.Achievements.UpdateUserAchievement(userAchievement.ID, &gitlab.UpdateUserAchievementOptions{
		ShowOnProfile: gitlab.Ptr(false),
	})
	require.NoError(t, err, "UpdateUserAchievement failed")
	require.NotNil(t, updatedUserAchievement)

	// THEN the user achievement should reflect the updated flag
	assert.False(t, updatedUserAchievement.ShowOnProfile, "Field: ShowOnProfile")

	// STEP 6: Update user achievement priorities
	// WHEN reordering the user's achievements (single element, priority 0)
	// (also only permitted for the achievement recipient)
	reordered, _, err := asRecipient.Achievements.UpdateUserAchievementPriorities([]int64{userAchievement.ID})
	require.NoError(t, err, "UpdateUserAchievementPriorities failed")
	require.Len(t, reordered, 1)
	assert.Equal(t, userAchievement.ID, reordered[0].ID, "Field: ID")

	// STEP 7: Revoke the achievement from the user
	// WHEN revoking the awarded achievement
	revokedUserAchievement, _, err := client.Achievements.RevokeAchievement(userAchievement.ID)
	require.NoError(t, err, "RevokeAchievement failed")
	require.NotNil(t, revokedUserAchievement)

	// THEN the user achievement should be marked as revoked
	assert.NotNil(t, revokedUserAchievement.RevokedAt, "Field: RevokedAt")
	assert.NotNil(t, revokedUserAchievement.RevokedByUserID, "Field: RevokedByUserID")

	// STEP 8: Delete the user achievement record entirely
	// WHEN deleting the user achievement
	deletedUserAchievement, _, err := client.Achievements.DeleteUserAchievement(userAchievement.ID)
	require.NoError(t, err, "DeleteUserAchievement failed")
	require.NotNil(t, deletedUserAchievement)

	// STEP 9: Delete the achievement itself
	// WHEN deleting the achievement
	deletedAchievement, _, err := client.Achievements.DeleteAchievement(achievement.ID)
	require.NoError(t, err, "DeleteAchievement failed")
	require.NotNil(t, deletedAchievement)

	// THEN the achievement should no longer be usable (further mutations fail)
	_, _, err = client.Achievements.UpdateAchievement(achievement.ID, &gitlab.UpdateAchievementOptions{Name: gitlab.Ptr("noop")})
	assert.Error(t, err, "expected UpdateAchievement to fail on a deleted achievement")
}
Loading