-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 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 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,61 @@ | ||
# Testing Guide | ||
|
||
This directory contains various tests for the CDN service. | ||
|
||
## Test Types | ||
|
||
### 1. Unit Tests | ||
Located in `unit/` directory, these test individual components in isolation. | ||
```bash | ||
go test ./test/unit/... -v | ||
``` | ||
|
||
### 2. Integration Tests | ||
Located in `integration/` directory, these test API endpoints with real HTTP calls. | ||
```bash | ||
go test ./test/integration/... -v | ||
``` | ||
|
||
### 3. Load Tests | ||
Located in `performance/` directory, using k6 for load testing. | ||
```bash | ||
k6 run test/performance/load_test.js | ||
``` | ||
|
||
## Prerequisites | ||
|
||
- Go 1.21 or higher | ||
- k6 for load testing | ||
- Docker for integration tests | ||
- `testify` package for assertions | ||
|
||
## Running Tests | ||
|
||
### All Tests | ||
```bash | ||
make test | ||
``` | ||
|
||
### Unit Tests with Coverage | ||
```bash | ||
go test ./test/unit/... -coverprofile=coverage.out | ||
go tool cover -html=coverage.out | ||
``` | ||
|
||
### Load Testing Scenarios | ||
|
||
1. Basic Load Test: | ||
```bash | ||
k6 run test/performance/load_test.js | ||
``` | ||
|
||
2. Stress Test (modify options in script): | ||
```bash | ||
k6 run --vus 50 --duration 5m test/performance/load_test.js | ||
``` | ||
|
||
## Test Data | ||
|
||
- Sample test files are in `test/data/` | ||
- Mock services are in respective test directories | ||
- Environment variables for tests in `.env.test` |
This file contains 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,63 @@ | ||
package integration | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"net/http" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const ( | ||
baseURL = "http://localhost:9090" | ||
timeout = 10 * time.Second | ||
) | ||
|
||
func TestHealthEndpoint(t *testing.T) { | ||
client := &http.Client{Timeout: timeout} | ||
|
||
resp, err := client.Get(baseURL + "/health") | ||
assert.NoError(t, err) | ||
defer resp.Body.Close() | ||
|
||
assert.Equal(t, http.StatusOK, resp.StatusCode) | ||
|
||
var body map[string]interface{} | ||
err = json.NewDecoder(resp.Body).Decode(&body) | ||
assert.NoError(t, err) | ||
|
||
assert.Equal(t, true, body["status"]) | ||
assert.Equal(t, "Healthy", body["message"]) | ||
} | ||
|
||
func TestUploadEndpoint(t *testing.T) { | ||
client := &http.Client{Timeout: timeout} | ||
|
||
// Test file upload | ||
fileContents := []byte("test image content") | ||
req, err := http.NewRequest("POST", baseURL+"/upload", bytes.NewBuffer(fileContents)) | ||
assert.NoError(t, err) | ||
|
||
req.Header.Set("Content-Type", "multipart/form-data") | ||
req.Header.Set("Authorization", "test-token") | ||
|
||
resp, err := client.Do(req) | ||
assert.NoError(t, err) | ||
defer resp.Body.Close() | ||
|
||
// Should fail without proper form data | ||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode) | ||
} | ||
|
||
func TestMetricsEndpoint(t *testing.T) { | ||
client := &http.Client{Timeout: timeout} | ||
|
||
resp, err := client.Get(baseURL + "/metrics") | ||
assert.NoError(t, err) | ||
defer resp.Body.Close() | ||
|
||
assert.Equal(t, http.StatusOK, resp.StatusCode) | ||
assert.Contains(t, resp.Header.Get("Content-Type"), "text/plain") | ||
} |
This file contains 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,45 @@ | ||
import http from 'k6/http'; | ||
import { check, sleep } from 'k6'; | ||
|
||
export let options = { | ||
stages: [ | ||
{ duration: '30s', target: 20 }, // Ramp up to 20 users | ||
{ duration: '1m', target: 20 }, // Stay at 20 users | ||
{ duration: '30s', target: 0 }, // Ramp down to 0 users | ||
], | ||
thresholds: { | ||
http_req_duration: ['p(95)<500'], // 95% of requests should be below 500ms | ||
http_req_failed: ['rate<0.01'], // Less than 1% of requests should fail | ||
}, | ||
}; | ||
|
||
const BASE_URL = 'http://localhost:9090'; | ||
|
||
export default function () { | ||
// Health check | ||
let healthCheck = http.get(`${BASE_URL}/health`); | ||
check(healthCheck, { | ||
'health check status is 200': (r) => r.status === 200, | ||
'health check response is healthy': (r) => r.json().status === true, | ||
}); | ||
|
||
// Metrics check | ||
let metrics = http.get(`${BASE_URL}/metrics`); | ||
check(metrics, { | ||
'metrics status is 200': (r) => r.status === 200, | ||
}); | ||
|
||
// Upload test (with small file) | ||
let testFile = open('./test.jpg', 'b'); | ||
let uploadData = { | ||
file: http.file(testFile, 'test.jpg'), | ||
bucket: 'test-bucket', | ||
}; | ||
|
||
let upload = http.post(`${BASE_URL}/upload`, uploadData); | ||
check(upload, { | ||
'upload status is 200': (r) => r.status === 200, | ||
}); | ||
|
||
sleep(1); | ||
} |
This file contains 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,129 @@ | ||
package unit | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/gofiber/fiber/v2" | ||
"github.com/minio/minio-go/v7" | ||
"github.com/minio/minio-go/v7/pkg/credentials" | ||
"github.com/mstgnz/cdn/handler" | ||
"github.com/mstgnz/cdn/service" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
) | ||
|
||
func setupMockMinio() *minio.Client { | ||
client, err := minio.New("localhost:9000", &minio.Options{ | ||
Creds: credentials.NewStaticV4("minioadmin", "minioadmin", ""), | ||
Secure: false, | ||
}) | ||
if err != nil { | ||
return nil | ||
} | ||
return client | ||
} | ||
|
||
type MockAwsService struct { | ||
mock.Mock | ||
service.AwsService | ||
} | ||
|
||
type MockCacheService struct { | ||
mock.Mock | ||
service.CacheService | ||
} | ||
|
||
func TestHealthCheck(t *testing.T) { | ||
// Setup | ||
app := fiber.New() | ||
mockMinio := setupMockMinio() | ||
mockAws := &MockAwsService{} | ||
mockCache := &MockCacheService{} | ||
|
||
healthChecker := handler.NewHealthChecker(mockMinio, mockAws, mockCache) | ||
app.Get("/health", healthChecker.HealthCheck) | ||
|
||
// Test cases | ||
tests := []struct { | ||
name string | ||
expectedStatus int | ||
expectedBody map[string]interface{} | ||
}{ | ||
{ | ||
name: "Success Response", | ||
expectedStatus: fiber.StatusOK, | ||
expectedBody: map[string]interface{}{ | ||
"status": true, | ||
"message": "Healthy", | ||
"data": map[string]interface{}{ | ||
"minio": "Connected", | ||
"aws": "Connected", | ||
"redis": "Connected", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
req := httptest.NewRequest("GET", "/health", nil) | ||
resp, err := app.Test(req) | ||
|
||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expectedStatus, resp.StatusCode) | ||
|
||
var body map[string]interface{} | ||
err = json.NewDecoder(resp.Body).Decode(&body) | ||
|
||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expectedBody, body) | ||
}) | ||
} | ||
} | ||
|
||
func TestUploadImage(t *testing.T) { | ||
// Setup | ||
app := fiber.New() | ||
mockMinio := setupMockMinio() | ||
mockAws := &MockAwsService{} | ||
|
||
imageHandler := handler.NewImage(mockMinio, mockAws) | ||
app.Post("/upload", imageHandler.UploadImage) | ||
|
||
// Test cases | ||
tests := []struct { | ||
name string | ||
payload []byte | ||
expectedStatus int | ||
expectedError string | ||
}{ | ||
{ | ||
name: "Invalid Request", | ||
payload: []byte(`{}`), | ||
expectedStatus: fiber.StatusBadRequest, | ||
expectedError: "Invalid request", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
req := httptest.NewRequest("POST", "/upload", bytes.NewBuffer(tt.payload)) | ||
req.Header.Set("Content-Type", "application/json") | ||
|
||
resp, err := app.Test(req) | ||
|
||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expectedStatus, resp.StatusCode) | ||
|
||
if tt.expectedError != "" { | ||
var body map[string]interface{} | ||
err = json.NewDecoder(resp.Body).Decode(&body) | ||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expectedError, body["message"]) | ||
} | ||
}) | ||
} | ||
} |