edge-connect-client/sdk/edgeconnect/apps_test.go

319 lines
7.7 KiB
Go

// ABOUTME: Unit tests for App management APIs using httptest mock server
// ABOUTME: Tests create, show, list, and delete operations with error conditions
package edgeconnect
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreateApp(t *testing.T) {
tests := []struct {
name string
input *NewAppInput
mockStatusCode int
mockResponse string
expectError bool
}{
{
name: "successful creation",
input: &NewAppInput{
Region: "us-west",
App: App{
Key: AppKey{
Organization: "testorg",
Name: "testapp",
Version: "1.0.0",
},
Deployment: "kubernetes",
},
},
mockStatusCode: 200,
mockResponse: `{"message": "success"}`,
expectError: false,
},
{
name: "validation error",
input: &NewAppInput{
Region: "us-west",
App: App{
Key: AppKey{
Organization: "",
Name: "testapp",
Version: "1.0.0",
},
},
},
mockStatusCode: 400,
mockResponse: `{"message": "organization is required"}`,
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create mock server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
assert.Equal(t, "/api/v1/auth/ctrl/CreateApp", r.URL.Path)
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
w.WriteHeader(tt.mockStatusCode)
w.Write([]byte(tt.mockResponse))
}))
defer server.Close()
// Create client
client := NewClient(server.URL,
WithHTTPClient(&http.Client{Timeout: 5 * time.Second}),
WithAuthProvider(NewStaticTokenProvider("test-token")),
)
// Execute test
ctx := context.Background()
err := client.CreateApp(ctx, tt.input)
// Verify results
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestShowApp(t *testing.T) {
tests := []struct {
name string
appKey AppKey
region string
mockStatusCode int
mockResponse string
expectError bool
expectNotFound bool
}{
{
name: "successful show",
appKey: AppKey{
Organization: "testorg",
Name: "testapp",
Version: "1.0.0",
},
region: "us-west",
mockStatusCode: 200,
mockResponse: `{"data": {"key": {"organization": "testorg", "name": "testapp", "version": "1.0.0"}, "deployment": "kubernetes"}}
`,
expectError: false,
expectNotFound: false,
},
{
name: "app not found",
appKey: AppKey{
Organization: "testorg",
Name: "nonexistent",
Version: "1.0.0",
},
region: "us-west",
mockStatusCode: 404,
mockResponse: "",
expectError: true,
expectNotFound: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create mock server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
assert.Equal(t, "/api/v1/auth/ctrl/ShowApp", r.URL.Path)
w.WriteHeader(tt.mockStatusCode)
if tt.mockResponse != "" {
w.Write([]byte(tt.mockResponse))
}
}))
defer server.Close()
// Create client
client := NewClient(server.URL,
WithHTTPClient(&http.Client{Timeout: 5 * time.Second}),
)
// Execute test
ctx := context.Background()
app, err := client.ShowApp(ctx, tt.appKey, tt.region)
// Verify results
if tt.expectError {
assert.Error(t, err)
if tt.expectNotFound {
assert.Contains(t, err.Error(), "resource not found")
}
} else {
require.NoError(t, err)
assert.Equal(t, tt.appKey.Organization, app.Key.Organization)
assert.Equal(t, tt.appKey.Name, app.Key.Name)
assert.Equal(t, tt.appKey.Version, app.Key.Version)
}
})
}
}
func TestShowApps(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
assert.Equal(t, "/api/v1/auth/ctrl/ShowApp", r.URL.Path)
// Verify request body
var filter AppFilter
err := json.NewDecoder(r.Body).Decode(&filter)
require.NoError(t, err)
assert.Equal(t, "testorg", filter.App.Key.Organization)
assert.Equal(t, "us-west", filter.Region)
// Return multiple apps
response := `{"data": {"key": {"organization": "testorg", "name": "app1", "version": "1.0.0"}, "deployment": "kubernetes"}}
{"data": {"key": {"organization": "testorg", "name": "app2", "version": "1.0.0"}, "deployment": "docker"}}
`
w.WriteHeader(200)
w.Write([]byte(response))
}))
defer server.Close()
client := NewClient(server.URL)
ctx := context.Background()
apps, err := client.ShowApps(ctx, AppKey{Organization: "testorg"}, "us-west")
require.NoError(t, err)
assert.Len(t, apps, 2)
assert.Equal(t, "app1", apps[0].Key.Name)
assert.Equal(t, "app2", apps[1].Key.Name)
}
func TestDeleteApp(t *testing.T) {
tests := []struct {
name string
appKey AppKey
region string
mockStatusCode int
expectError bool
}{
{
name: "successful deletion",
appKey: AppKey{
Organization: "testorg",
Name: "testapp",
Version: "1.0.0",
},
region: "us-west",
mockStatusCode: 200,
expectError: false,
},
{
name: "already deleted (404 ok)",
appKey: AppKey{
Organization: "testorg",
Name: "testapp",
Version: "1.0.0",
},
region: "us-west",
mockStatusCode: 404,
expectError: false,
},
{
name: "server error",
appKey: AppKey{
Organization: "testorg",
Name: "testapp",
Version: "1.0.0",
},
region: "us-west",
mockStatusCode: 500,
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
assert.Equal(t, "/api/v1/auth/ctrl/DeleteApp", r.URL.Path)
w.WriteHeader(tt.mockStatusCode)
}))
defer server.Close()
client := NewClient(server.URL)
ctx := context.Background()
err := client.DeleteApp(ctx, tt.appKey, tt.region)
if tt.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestClientOptions(t *testing.T) {
t.Run("with auth provider", func(t *testing.T) {
authProvider := NewStaticTokenProvider("test-token")
client := NewClient("https://example.com",
WithAuthProvider(authProvider),
)
assert.Equal(t, authProvider, client.AuthProvider)
})
t.Run("with custom HTTP client", func(t *testing.T) {
httpClient := &http.Client{Timeout: 10 * time.Second}
client := NewClient("https://example.com",
WithHTTPClient(httpClient),
)
assert.Equal(t, httpClient, client.HTTPClient)
})
t.Run("with retry options", func(t *testing.T) {
retryOpts := RetryOptions{MaxRetries: 5}
client := NewClient("https://example.com",
WithRetryOptions(retryOpts),
)
assert.Equal(t, 5, client.RetryOpts.MaxRetries)
})
}
func TestAPIError(t *testing.T) {
err := &APIError{
StatusCode: 400,
Messages: []string{"validation failed", "name is required"},
}
assert.Contains(t, err.Error(), "validation failed")
assert.Equal(t, 400, err.StatusCode)
assert.Len(t, err.Messages, 2)
}
// Helper function to create a test server that handles streaming JSON responses
func createStreamingJSONServer(responses []string, statusCode int) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(statusCode)
for _, response := range responses {
w.Write([]byte(response + "\n"))
}
}))
}