-
Notifications
You must be signed in to change notification settings - Fork 268
Add unit tests for internal/service module #3781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
salignatmoandal
wants to merge
5
commits into
supabase:develop
Choose a base branch
from
salignatmoandal:test/check-versions
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
ca24d8a
Add unit tests for internal/service module
salignatmoandal dad4a5c
update test
salignatmoandal 843b8f1
mock all API request using gock to fix
salignatmoandal 0360c7b
update hardcoded credential
salignatmoandal 9f11335
Fix race condition in listRemoteImages by protecting map writes with …
salignatmoandal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,335 @@ | ||
package services | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/h2non/gock" | ||
"github.com/spf13/afero" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"github.com/supabase/cli/internal/utils" | ||
"github.com/supabase/cli/internal/utils/flags" | ||
) | ||
|
||
// TestRun tests the main Run function that displays service versions | ||
func TestRun(t *testing.T) { | ||
// Test case: Display service versions without linked project | ||
t.Run("displays service versions without linked project", func(t *testing.T) { | ||
// Setup: Create an in-memory filesystem | ||
fsys := afero.NewMemMapFs() | ||
|
||
// Execute: Call the Run function | ||
err := Run(context.Background(), fsys) | ||
|
||
// Verify: Check that no error occurred | ||
assert.NoError(t, err) | ||
}) | ||
|
||
// Test case: Display service versions with linked project | ||
t.Run("displays service versions with linked project", func(t *testing.T) { | ||
// Setup: Create an in-memory filesystem and simulate linked project | ||
fsys := afero.NewMemMapFs() | ||
|
||
// Create project config file with project reference | ||
projectRef := "abcdefghijklmnopqrst" | ||
require.NoError(t, utils.InitConfig(utils.InitParams{ | ||
ProjectId: projectRef, | ||
}, fsys)) | ||
flags.ProjectRef = projectRef | ||
|
||
// Mock all API requests | ||
defer gock.OffAll() | ||
|
||
// Mock API keys | ||
gock.New(utils.DefaultApiHost). | ||
Get("/v1/projects/" + projectRef + "/api-keys"). | ||
Reply(200). | ||
JSON([]map[string]string{{"name": "anon", "api_key": "test-key"}}) | ||
|
||
// Mock database version | ||
gock.New(utils.DefaultApiHost). | ||
Get("/v1/projects"). | ||
Reply(200). | ||
JSON([]map[string]interface{}{ | ||
{ | ||
"id": projectRef, | ||
"database": map[string]string{"version": "1.0.0"}, | ||
}, | ||
}) | ||
|
||
// Mock auth version | ||
gock.New("https://" + utils.GetSupabaseHost(projectRef)). | ||
Get("/auth/v1/health"). | ||
Reply(200). | ||
JSON(map[string]string{"version": "2.0.0"}) | ||
|
||
// Mock postgrest version | ||
gock.New("https://" + utils.GetSupabaseHost(projectRef)). | ||
Get("/rest/v1/"). | ||
Reply(200). | ||
JSON(map[string]interface{}{ | ||
"swagger": "2.0", | ||
"info": map[string]string{"version": "3.0.0"}, | ||
}) | ||
|
||
// Execute: Call the Run function | ||
err := Run(context.Background(), fsys) | ||
|
||
// Verify: Check that no error occurred | ||
assert.NoError(t, err) | ||
}) | ||
} | ||
|
||
// TestCheckVersions tests the function that checks local and remote service versions | ||
func TestCheckVersions(t *testing.T) { | ||
// Test case: Check local versions only | ||
t.Run("checks local versions", func(t *testing.T) { | ||
// Setup: Create an in-memory filesystem | ||
fsys := afero.NewMemMapFs() | ||
|
||
// Execute: Call CheckVersions function | ||
versions := CheckVersions(context.Background(), fsys) | ||
|
||
// Verify: Check that versions are returned and contain required fields | ||
assert.NotEmpty(t, versions) | ||
for _, v := range versions { | ||
assert.NotEmpty(t, v.Name, "Service name should not be empty") | ||
assert.NotEmpty(t, v.Local, "Local version should not be empty") | ||
} | ||
}) | ||
|
||
// Test case: Check both local and remote versions | ||
t.Run("checks local and remote versions", func(t *testing.T) { | ||
// Setup: Create an in-memory filesystem and simulate linked project | ||
fsys := afero.NewMemMapFs() | ||
|
||
// Create project config file with project reference | ||
projectRef := "abcdefghijklmnopqrst" | ||
require.NoError(t, utils.InitConfig(utils.InitParams{ | ||
ProjectId: projectRef, | ||
}, fsys)) | ||
|
||
// Set project reference in flags | ||
flags.ProjectRef = projectRef | ||
|
||
// Execute: Call CheckVersions function | ||
versions := CheckVersions(context.Background(), fsys) | ||
|
||
// Verify: Check that versions are returned and contain required fields | ||
assert.NotEmpty(t, versions) | ||
for _, v := range versions { | ||
assert.NotEmpty(t, v.Name, "Service name should not be empty") | ||
assert.NotEmpty(t, v.Local, "Local version should not be empty") | ||
// Remote version might be empty if not linked | ||
} | ||
}) | ||
|
||
// Test case: Handle version mismatch | ||
t.Run("handles version mismatch", func(t *testing.T) { | ||
// Setup: Create an in-memory filesystem and simulate linked project | ||
fsys := afero.NewMemMapFs() | ||
|
||
// Create project config file with project reference | ||
projectRef := "abcdefghijklmnopqrst" | ||
require.NoError(t, utils.InitConfig(utils.InitParams{ | ||
ProjectId: projectRef, | ||
}, fsys)) | ||
|
||
// Set project reference in flags | ||
flags.ProjectRef = projectRef | ||
|
||
// Execute: Call CheckVersions function | ||
versions := CheckVersions(context.Background(), fsys) | ||
|
||
// Verify: Check that versions are returned and contain required fields | ||
assert.NotEmpty(t, versions) | ||
for _, v := range versions { | ||
assert.NotEmpty(t, v.Name, "Service name should not be empty") | ||
assert.NotEmpty(t, v.Local, "Local version should not be empty") | ||
// Remote version might be empty if not linked | ||
} | ||
}) | ||
|
||
// Test case: Verify version comparison logic | ||
t.Run("compares local and remote versions correctly", func(t *testing.T) { | ||
fsys := afero.NewMemMapFs() | ||
projectRef := "abcdefghijklmnopqrst" | ||
|
||
// Setup: Create linked project with specific versions | ||
require.NoError(t, utils.InitConfig(utils.InitParams{ | ||
ProjectId: projectRef, | ||
}, fsys)) | ||
flags.ProjectRef = projectRef | ||
|
||
// Mock remote versions | ||
// #nosec G101 -- This is a fake token for testing purposes only | ||
token := "sbp_0102030405060708091011121314151617181920" | ||
require.NoError(t, utils.SaveAccessToken(token, fsys)) | ||
|
||
defer gock.OffAll() | ||
// Mock API responses with specific versions | ||
gock.New(utils.DefaultApiHost). | ||
Get("/v1/projects/" + projectRef + "/api-keys"). | ||
Reply(200). | ||
JSON([]map[string]string{{"name": "anon", "api_key": "test-key"}}) | ||
|
||
gock.New(utils.DefaultApiHost). | ||
Get("/v1/projects/" + projectRef + "/database/version"). | ||
Reply(200). | ||
JSON(map[string]string{"version": "1.0.0"}) | ||
|
||
versions := CheckVersions(context.Background(), fsys) | ||
|
||
// Verify version comparison logic | ||
for _, v := range versions { | ||
assert.NotEmpty(t, v.Name) | ||
assert.NotEmpty(t, v.Local) | ||
// Check if remote versions are properly assigned | ||
} | ||
}) | ||
} | ||
|
||
// TestListRemoteImages tests the function that retrieves remote service versions | ||
func TestListRemoteImages(t *testing.T) { | ||
// Test case: Get remote versions successfully | ||
t.Run("gets remote versions successfully", func(t *testing.T) { | ||
// Setup: Create context and project reference | ||
ctx := context.Background() | ||
projectRef := "abcdefghijklmnopqrst" | ||
|
||
// Setup: Create in-memory filesystem | ||
fsys := afero.NewMemMapFs() | ||
|
||
// Setup: Create access token file with valid format | ||
// #nosec G101 -- This is a fake token for testing purposes only | ||
token := "sbp_0102030405060708091011121314151617181920" | ||
require.NoError(t, utils.SaveAccessToken(token, fsys)) | ||
|
||
// Setup: Mock API responses | ||
defer gock.OffAll() | ||
|
||
// Mock API keys response | ||
gock.New(utils.DefaultApiHost). | ||
Get("/v1/projects/" + projectRef + "/api-keys"). | ||
Reply(200). | ||
JSON([]map[string]string{ | ||
{"name": "anon", "api_key": "test-key"}, | ||
}) | ||
|
||
gock.New(utils.DefaultApiHost). | ||
Get("/v1/projects"). | ||
Reply(200). | ||
JSON([]map[string]interface{}{ | ||
{ | ||
"id": projectRef, | ||
"database": map[string]string{ | ||
"version": "1.0.0", | ||
}, | ||
}, | ||
}) | ||
|
||
gock.New("https://" + utils.GetSupabaseHost(projectRef)). | ||
Get("/auth/v1/health"). | ||
Reply(200). | ||
JSON(map[string]string{"version": "2.0.0"}) | ||
|
||
// Mock postgrest version response (endpoint = /rest/v1/ sur le host du projet) | ||
gock.New("https://" + utils.GetSupabaseHost(projectRef)). | ||
Get("/rest/v1/"). | ||
Reply(200). | ||
JSON(map[string]interface{}{ | ||
"swagger": "2.0", | ||
"info": map[string]string{ | ||
"version": "3.0.0", | ||
}, | ||
}) | ||
|
||
// Execute: Call listRemoteImages function | ||
remoteVersions := listRemoteImages(ctx, projectRef) | ||
|
||
// Verify: Check that remote versions are returned | ||
assert.NotNil(t, remoteVersions) | ||
assert.NotEmpty(t, remoteVersions) | ||
|
||
// Verify: Check that all expected versions are present | ||
for _, version := range remoteVersions { | ||
assert.NotEmpty(t, version) | ||
} | ||
}) | ||
|
||
// Test case: Handle API errors | ||
t.Run("handles API errors", func(t *testing.T) { | ||
// Setup: Create context and project reference | ||
ctx := context.Background() | ||
projectRef := "invalid-project" | ||
|
||
// Setup: Create in-memory filesystem | ||
fsys := afero.NewMemMapFs() | ||
|
||
// Setup: Create access token file with valid format | ||
// #nosec G101 -- This is a fake token for testing purposes only | ||
token := "sbp_0102030405060708091011121314151617181920" | ||
require.NoError(t, utils.SaveAccessToken(token, fsys)) | ||
|
||
// Setup: Mock API error response | ||
defer gock.OffAll() | ||
gock.New(utils.DefaultApiHost). | ||
Get("/v1/projects/" + projectRef + "/api-keys"). | ||
Reply(404) | ||
|
||
// Execute: Call listRemoteImages function | ||
remoteVersions := listRemoteImages(ctx, projectRef) | ||
|
||
// Verify: Check that remote versions are empty | ||
assert.Empty(t, remoteVersions) | ||
}) | ||
|
||
// Test case: Handle missing access token | ||
t.Run("handles missing access token", func(t *testing.T) { | ||
// Setup: Create context and project reference | ||
ctx := context.Background() | ||
projectRef := "abcdefghijklmnopqrst" | ||
|
||
// Setup: Create in-memory filesystem without access token | ||
afero.NewMemMapFs() | ||
|
||
// Execute: Call listRemoteImages function | ||
remoteVersions := listRemoteImages(ctx, projectRef) | ||
|
||
// Verify: Check that remote versions are empty | ||
assert.Empty(t, remoteVersions) | ||
}) | ||
} | ||
|
||
// TestSuggestUpdateCmd tests the function that generates update command suggestions | ||
func TestSuggestUpdateCmd(t *testing.T) { | ||
// Test case: Generate update command for version mismatch | ||
t.Run("generates update command for version mismatch", func(t *testing.T) { | ||
// Setup: Create map of service images with version mismatches | ||
serviceImages := map[string]string{ | ||
"service1": "v1.0.0", | ||
"service2": "v2.0.0", | ||
} | ||
|
||
// Execute: Call suggestUpdateCmd function | ||
cmd := suggestUpdateCmd(serviceImages) | ||
|
||
// Verify: Check that command contains expected content | ||
assert.Contains(t, cmd, "WARNING:") | ||
assert.Contains(t, cmd, "supabase link") | ||
}) | ||
|
||
// Test case: Handle empty service images | ||
t.Run("handles empty service images", func(t *testing.T) { | ||
// Setup: Create empty map of service images | ||
serviceImages := map[string]string{} | ||
|
||
// Execute: Call suggestUpdateCmd function | ||
cmd := suggestUpdateCmd(serviceImages) | ||
|
||
// Verify: Check that command contains expected content | ||
assert.Contains(t, cmd, "WARNING:") | ||
assert.Contains(t, cmd, "supabase link") | ||
}) | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make these tests pass, you need to mock all api requests using gock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it — I’ll use gock to mock the API calls and make the tests pass. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-race
and are CI-friendly