Improved unit testing and integration tests

This commit is contained in:
benduncan
2025-10-06 14:28:41 +08:00
parent 9de19be3f0
commit 24e790e4e6
4 changed files with 1044 additions and 10 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ go_run:
test:
@echo "\n....Running tests for $(GO_PROJECT_NAME)...."
LOG_IGNORE=1 go test -v ./...
LOG_IGNORE=1 go test -v -timeout 300s ./...
bench:
@echo "\n....Running benchmarks for $(GO_PROJECT_NAME)...."
@@ -0,0 +1,681 @@
package predastore
// Integration tests for predastore service
//
// These tests start a real predastore daemon and test S3 operations against it.
// Tests use AWS SDK Go v1 to verify bucket operations, authentication, and file operations.
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io"
"math/big"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/stretchr/testify/assert"
)
// Test credentials from config
const (
validAccessKey = "AKIAIOSFODNN7EXAMPLE"
validSecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
invalidAccessKey = "INVALIDACCESSKEY"
invalidSecretKey = "InvalidSecretKey123"
testBucket = "test-bucket"
publicBucket = "public-bucket"
testRegion = "us-east-1"
testPort = 18443
testHost = "127.0.0.1"
)
// Global server instance for integration tests
var (
serverStarted bool
serverStartMutex sync.Mutex
sharedTestDir string
sharedConfig *Config
)
// generateTestCertificate generates a self-signed TLS certificate for testing
func generateTestCertificate(certPath, keyPath string) error {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return err
}
notBefore := time.Now()
notAfter := notBefore.Add(24 * time.Hour)
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return err
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{"Predastore Test"},
CommonName: "localhost",
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
DNSNames: []string{"localhost"},
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return err
}
certOut, err := os.Create(certPath)
if err != nil {
return err
}
defer certOut.Close()
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
return err
}
keyOut, err := os.Create(keyPath)
if err != nil {
return err
}
defer keyOut.Close()
privBytes, err := x509.MarshalECPrivateKey(priv)
if err != nil {
return err
}
return pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
}
// startPredastoreServer starts the predastore server once for all tests
func startPredastoreServer(t *testing.T) *Config {
serverStartMutex.Lock()
defer serverStartMutex.Unlock()
if serverStarted {
// Server already running, return shared config
return sharedConfig
}
// Create persistent test environment (not using t.TempDir() for shared server)
testDir, err := os.MkdirTemp("", "predastore-integration-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
sharedTestDir = testDir
// Create bucket directories
testBucketDir := filepath.Join(testDir, "test-bucket")
publicBucketDir := filepath.Join(testDir, "public-bucket")
err = os.MkdirAll(testBucketDir, 0755)
if err != nil {
t.Fatalf("Failed to create test-bucket dir: %v", err)
}
err = os.MkdirAll(publicBucketDir, 0755)
if err != nil {
t.Fatalf("Failed to create public-bucket dir: %v", err)
}
// Create TLS cert directory
certDir := filepath.Join(testDir, "certs")
err = os.MkdirAll(certDir, 0755)
if err != nil {
t.Fatalf("Failed to create cert dir: %v", err)
}
certPath := filepath.Join(certDir, "test.crt")
keyPath := filepath.Join(certDir, "test.key")
err = generateTestCertificate(certPath, keyPath)
if err != nil {
t.Fatalf("Failed to generate certificate: %v", err)
}
// Create config file
configPath := filepath.Join(testDir, "predastore_test.toml")
configContent := `version = "1.0"
region = "us-east-1"
host = "127.0.0.1"
port = 18443
debug = true
disable_logging = false
base_path = "` + testDir + `"
[[buckets]]
name = "test-bucket"
region = "us-east-1"
type = "fs"
pathname = "` + testDir + `test-bucket"
public = false
encryption = ""
[[buckets]]
name = "public-bucket"
region = "us-east-1"
type = "fs"
pathname = "` + testDir + `public-bucket"
public = true
encryption = ""
[[auth]]
access_key_id = "AKIAIOSFODNN7EXAMPLE"
secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
policy = [
{ bucket = "test-bucket", actions = ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListAllMyBuckets"] },
{ bucket = "public-bucket", actions = ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"] },
]
`
err = os.WriteFile(configPath, []byte(configContent), 0644)
if err != nil {
t.Fatalf("Failed to write config: %v", err)
}
cfg := &Config{
ConfigPath: configPath,
Port: 18443,
Host: "127.0.0.1",
Debug: true,
BasePath: testDir,
TlsCert: certPath,
TlsKey: keyPath,
}
sharedConfig = cfg
// Start server in background
go func() {
svc, _ := New(cfg)
svc.Start()
}()
// Wait for server to be ready
if !waitForServer(10 * time.Second) {
t.Fatal("Failed to start predastore server")
}
serverStarted = true
t.Logf("Predastore server started successfully")
t.Logf("Test directory: %s", testDir)
t.Logf("Bucket directories created: %s, %s", testBucketDir, publicBucketDir)
// Verify directories exist
if _, err := os.Stat(testBucketDir); os.IsNotExist(err) {
t.Fatalf("test-bucket directory does not exist: %s", testBucketDir)
}
if _, err := os.Stat(publicBucketDir); os.IsNotExist(err) {
t.Fatalf("public-bucket directory does not exist: %s", publicBucketDir)
}
t.Logf("Verified bucket directories exist")
// NOTE: Cleanup will happen automatically when test process exits
// Don't use t.Cleanup() as it runs after each test, not at the end
return cfg
}
// waitForServer waits for the predastore server to be ready
func waitForServer(timeout time.Duration) bool {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Timeout: 1 * time.Second,
}
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
resp, err := client.Get(fmt.Sprintf("https://%s:%d/", testHost, testPort))
if err == nil {
resp.Body.Close()
// Give a bit more time for config to load
time.Sleep(500 * time.Millisecond)
return true
}
time.Sleep(100 * time.Millisecond)
}
return false
}
// createS3Client creates an AWS S3 client for testing
func createS3Client(accessKey, secretKey string) *s3.S3 {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
httpClient := &http.Client{Transport: tr}
sess := session.Must(session.NewSession(&aws.Config{
Region: aws.String(testRegion),
Endpoint: aws.String(fmt.Sprintf("https://%s:%d", testHost, testPort)),
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
S3ForcePathStyle: aws.Bool(true),
DisableSSL: aws.Bool(false),
HTTPClient: httpClient,
}))
return s3.New(sess)
}
// TestIntegration_BucketListing tests that both buckets from config exist
func TestIntegration_BucketListing(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
// Start server (only starts once)
startPredastoreServer(t)
// Create S3 client
client := createS3Client(validAccessKey, validSecretKey)
// List all buckets
result, err := client.ListBuckets(&s3.ListBucketsInput{})
assert.NoError(t, err, "ListBuckets should not return error")
assert.NotNil(t, result, "ListBuckets result should not be nil")
// Verify both buckets exist
buckets := make(map[string]bool)
for _, bucket := range result.Buckets {
buckets[*bucket.Name] = true
t.Logf("Found bucket: %s", *bucket.Name)
}
//assert.True(t, buckets[testBucket], "test-bucket should exist")
//assert.True(t, buckets[publicBucket], "public-bucket should exist")
assert.Len(t, buckets, 2, "Should have exactly 2 buckets")
}
// TestIntegration_Authentication_Valid tests authentication with valid credentials
func TestIntegration_Authentication_Valid(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
// Create client with valid credentials
client := createS3Client(validAccessKey, validSecretKey)
// Try to list buckets
result, err := client.ListBuckets(&s3.ListBucketsInput{})
assert.NoError(t, err, "Valid credentials should authenticate successfully")
if assert.NotNil(t, result, "Result should not be nil") {
// Just verify authentication worked - bucket count is verified in BucketListing test
t.Logf("Authentication successful, found %d buckets", len(result.Buckets))
}
}
// TestIntegration_Authentication_Invalid tests authentication with invalid credentials
func TestIntegration_Authentication_Invalid(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
// Create client with invalid credentials
client := createS3Client(invalidAccessKey, invalidSecretKey)
// Try to list buckets - should fail
_, err := client.ListBuckets(&s3.ListBucketsInput{})
assert.Error(t, err, "Invalid credentials should fail authentication")
// Verify error contains authentication-related message
errStr := err.Error()
assert.True(t,
strings.Contains(errStr, "InvalidAccessKeyId") ||
strings.Contains(errStr, "SignatureDoesNotMatch") ||
strings.Contains(errStr, "Forbidden") ||
strings.Contains(errStr, "403"),
"Error should indicate authentication failure, got: %s", errStr)
}
// TestIntegration_FileUpload_TestBucket tests file upload to test-bucket
func TestIntegration_FileUpload_TestBucket(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
client := createS3Client(validAccessKey, validSecretKey)
// Upload test file
key := "test1.txt"
expectedContent := "hello-world-test-1-" + testBucket
body := strings.NewReader(expectedContent)
_, err := client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(testBucket),
Key: aws.String(key),
Body: body,
})
assert.NoError(t, err, "File upload should succeed")
// Download and verify content
result, err := client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(testBucket),
Key: aws.String(key),
})
if !assert.NoError(t, err, "File download should succeed") {
return // Skip rest if download failed
}
if !assert.NotNil(t, result, "Result should not be nil") {
return
}
defer result.Body.Close()
downloadedContent, err := io.ReadAll(result.Body)
assert.NoError(t, err, "Reading file content should succeed")
assert.Equal(t, expectedContent, string(downloadedContent), "File content should match")
t.Logf("Successfully uploaded and verified file: s3://%s/%s", testBucket, key)
}
// TestIntegration_FileUpload_PublicBucket tests file upload to public-bucket
func TestIntegration_FileUpload_PublicBucket(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
client := createS3Client(validAccessKey, validSecretKey)
// Upload test file
key := "test1.txt"
expectedContent := "hello-world-test-1-" + publicBucket
body := strings.NewReader(expectedContent)
_, err := client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(publicBucket),
Key: aws.String(key),
Body: body,
})
assert.NoError(t, err, "File upload should succeed")
// Download and verify content
result, err := client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(publicBucket),
Key: aws.String(key),
})
if !assert.NoError(t, err, "File download should succeed") {
return
}
if !assert.NotNil(t, result, "Result should not be nil") {
return
}
defer result.Body.Close()
downloadedContent, err := io.ReadAll(result.Body)
assert.NoError(t, err, "Reading file content should succeed")
assert.Equal(t, expectedContent, string(downloadedContent), "File content should match")
t.Logf("Successfully uploaded and verified file: s3://%s/%s", publicBucket, key)
}
func TestIntegration_FileUpload_PublicBucket2(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
client := createS3Client(validAccessKey, validSecretKey)
time.Sleep(20 * time.Second)
// Upload test file
key := "test1.txt"
expectedContent := "hello-world-test-1-" + publicBucket
body := strings.NewReader(expectedContent)
_, err := client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(publicBucket),
Key: aws.String(key),
Body: body,
})
assert.NoError(t, err, "File upload should succeed")
// Download and verify content
result, err := client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(publicBucket),
Key: aws.String(key),
})
if !assert.NoError(t, err, "File download should succeed") {
return
}
if !assert.NotNil(t, result, "Result should not be nil") {
return
}
time.Sleep(30 * time.Second)
defer result.Body.Close()
downloadedContent, err := io.ReadAll(result.Body)
assert.NoError(t, err, "Reading file content should succeed")
assert.Equal(t, expectedContent, string(downloadedContent), "File content should match")
t.Logf("Successfully uploaded and verified file: s3://%s/%s", publicBucket, key)
}
// TestIntegration_FileOperations_Complete tests full file lifecycle
func TestIntegration_FileOperations_Complete(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
client := createS3Client(validAccessKey, validSecretKey)
// Test with both buckets
buckets := []string{testBucket, publicBucket}
for _, bucket := range buckets {
t.Run(bucket, func(t *testing.T) {
key := "lifecycle-test.txt"
content := fmt.Sprintf("hello-world-test-1-%s", bucket)
// 1. Upload file
_, err := client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
Body: strings.NewReader(content),
})
assert.NoError(t, err, "Upload should succeed")
// 2. Verify file exists in listing
listResult, err := client.ListObjectsV2(&s3.ListObjectsV2Input{
Bucket: aws.String(bucket),
})
assert.NoError(t, err, "List should succeed")
found := false
for _, obj := range listResult.Contents {
if *obj.Key == key {
found = true
break
}
}
assert.True(t, found, "File should appear in bucket listing")
// 3. Download and verify content
getResult, err := client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
})
if !assert.NoError(t, err, "Download should succeed") {
return
}
if !assert.NotNil(t, getResult, "Get result should not be nil") {
return
}
downloadedContent, err := io.ReadAll(getResult.Body)
getResult.Body.Close()
assert.NoError(t, err, "Reading content should succeed")
assert.Equal(t, content, string(downloadedContent), "Content should match")
// 4. Delete file
_, err = client.DeleteObject(&s3.DeleteObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
})
assert.NoError(t, err, "Delete should succeed")
// 5. Verify file no longer exists
listResult2, err := client.ListObjectsV2(&s3.ListObjectsV2Input{
Bucket: aws.String(bucket),
})
assert.NoError(t, err, "List after delete should succeed")
found = false
for _, obj := range listResult2.Contents {
if *obj.Key == key {
found = true
break
}
}
assert.False(t, found, "File should not appear in listing after delete")
// 6. Verify GET returns error for deleted file
_, err = client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
})
assert.Error(t, err, "GET on deleted file should return error")
t.Logf("Complete lifecycle test passed for bucket: %s", bucket)
})
}
}
// TestIntegration_MultipleFiles tests uploading multiple files
func TestIntegration_MultipleFiles(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
client := createS3Client(validAccessKey, validSecretKey)
// Upload multiple files to test-bucket
fileCount := 5
keys := make([]string, fileCount)
for i := 0; i < fileCount; i++ {
keys[i] = fmt.Sprintf("multi-test-%d.txt", i)
content := fmt.Sprintf("content-file-%d", i)
_, err := client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(testBucket),
Key: aws.String(keys[i]),
Body: strings.NewReader(content),
})
assert.NoError(t, err, "Upload file %d should succeed", i)
}
// List and verify all files exist
listResult, err := client.ListObjectsV2(&s3.ListObjectsV2Input{
Bucket: aws.String(testBucket),
})
assert.NoError(t, err, "List should succeed")
foundCount := 0
for _, obj := range listResult.Contents {
for _, key := range keys {
if *obj.Key == key {
foundCount++
break
}
}
}
assert.Equal(t, fileCount, foundCount, "All uploaded files should be listed")
// Cleanup - delete all test files
for _, key := range keys {
client.DeleteObject(&s3.DeleteObjectInput{
Bucket: aws.String(testBucket),
Key: aws.String(key),
})
}
}
// TestIntegration_LargeFile tests uploading a larger file (1MB)
func TestIntegration_LargeFile(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
startPredastoreServer(t)
client := createS3Client(validAccessKey, validSecretKey)
// Create 1MB file
size := 1024 * 1024
largeData := bytes.Repeat([]byte("A"), size)
key := "large-file-test.bin"
// Upload
_, err := client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(testBucket),
Key: aws.String(key),
Body: bytes.NewReader(largeData),
})
assert.NoError(t, err, "Large file upload should succeed")
// Download and verify
result, err := client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(testBucket),
Key: aws.String(key),
})
if !assert.NoError(t, err, "Large file download should succeed") {
return
}
if !assert.NotNil(t, result, "Result should not be nil") {
return
}
downloadedData, err := io.ReadAll(result.Body)
result.Body.Close()
assert.NoError(t, err, "Reading large file should succeed")
assert.Equal(t, size, len(downloadedData), "Downloaded size should match")
assert.Equal(t, largeData, downloadedData, "Downloaded content should match")
// Cleanup
client.DeleteObject(&s3.DeleteObjectInput{
Bucket: aws.String(testBucket),
Key: aws.String(key),
})
t.Logf("Successfully uploaded and verified 1MB file")
}
+351
View File
@@ -0,0 +1,351 @@
package predastore
import (
"testing"
"github.com/stretchr/testify/assert"
)
// TestNew tests the service constructor
func TestNew(t *testing.T) {
cfg := &Config{
ConfigPath: "/tmp/test-config.toml",
Port: 8443,
Host: "0.0.0.0",
Debug: false,
BasePath: "/tmp/predastore",
TlsCert: "/tmp/cert.pem",
TlsKey: "/tmp/key.pem",
}
svc, err := New(cfg)
assert.NoError(t, err)
assert.NotNil(t, svc)
assert.NotNil(t, svc.Config)
assert.Equal(t, "/tmp/test-config.toml", svc.Config.ConfigPath)
assert.Equal(t, 8443, svc.Config.Port)
assert.Equal(t, "0.0.0.0", svc.Config.Host)
assert.False(t, svc.Config.Debug)
assert.Equal(t, "/tmp/predastore", svc.Config.BasePath)
assert.Equal(t, "/tmp/cert.pem", svc.Config.TlsCert)
assert.Equal(t, "/tmp/key.pem", svc.Config.TlsKey)
}
// TestNewWithNilConfig tests that New handles nil config correctly
func TestNewWithNilConfig(t *testing.T) {
cfg := &Config{}
svc, err := New(cfg)
assert.NoError(t, err)
assert.NotNil(t, svc)
assert.NotNil(t, svc.Config)
}
// TestConfigDefaults tests Config struct default values
func TestConfigDefaults(t *testing.T) {
cfg := &Config{}
assert.Empty(t, cfg.ConfigPath)
assert.Empty(t, cfg.Host)
assert.Equal(t, 0, cfg.Port)
assert.False(t, cfg.Debug)
assert.Empty(t, cfg.BasePath)
assert.Empty(t, cfg.TlsCert)
assert.Empty(t, cfg.TlsKey)
}
// TestConfigWithDebug tests Config with debug enabled
func TestConfigWithDebug(t *testing.T) {
cfg := &Config{
Debug: true,
ConfigPath: "/etc/predastore/config.toml",
Port: 8443,
}
assert.True(t, cfg.Debug)
assert.Equal(t, "/etc/predastore/config.toml", cfg.ConfigPath)
assert.Equal(t, 8443, cfg.Port)
}
// TestConfigFullyPopulated tests a fully populated config
func TestConfigFullyPopulated(t *testing.T) {
cfg := &Config{
ConfigPath: "/etc/predastore/config.toml",
Port: 9443,
Host: "127.0.0.1",
Debug: true,
BasePath: "/var/lib/predastore",
TlsCert: "/etc/ssl/certs/predastore.crt",
TlsKey: "/etc/ssl/private/predastore.key",
}
assert.Equal(t, "/etc/predastore/config.toml", cfg.ConfigPath)
assert.Equal(t, 9443, cfg.Port)
assert.Equal(t, "127.0.0.1", cfg.Host)
assert.True(t, cfg.Debug)
assert.Equal(t, "/var/lib/predastore", cfg.BasePath)
assert.Equal(t, "/etc/ssl/certs/predastore.crt", cfg.TlsCert)
assert.Equal(t, "/etc/ssl/private/predastore.key", cfg.TlsKey)
}
// TestServiceMethods tests the service interface methods
func TestServiceMethods(t *testing.T) {
cfg := &Config{
ConfigPath: "/tmp/test-config.toml",
Port: 8443,
Host: "127.0.0.1",
}
svc, err := New(cfg)
assert.NoError(t, err)
// Test Status - returns empty string and no error
status, err := svc.Status()
assert.NoError(t, err)
assert.Empty(t, status)
// Test Reload - returns no error
err = svc.Reload()
assert.NoError(t, err)
}
// TestServiceShutdown tests that Shutdown calls Stop. Can't test, given it will exit the process and tests will fail.
/*
func TestServiceShutdown(t *testing.T) {
cfg := &Config{
ConfigPath: "/tmp/test-config.toml",
Port: 8443,
}
svc, err := New(cfg)
assert.NoError(t, err)
// Shutdown should call Stop internally
// Since we can't actually start the service, we just verify it doesn't panic
err = svc.Shutdown()
// Will error because no PID file exists, which is expected
assert.Error(t, err)
}
// TestServiceStop tests the Stop method
func TestServiceStop(t *testing.T) {
cfg := &Config{
ConfigPath: "/tmp/test-config.toml",
Port: 8443,
}
svc, err := New(cfg)
assert.NoError(t, err)
// Stop should try to read PID file and stop the process
err = svc.Stop()
// Will error because no PID file exists, which is expected in tests
assert.Error(t, err)
}
*/
// TestServiceNameConstant tests the serviceName constant
func TestServiceNameConstant(t *testing.T) {
assert.Equal(t, "predastore", serviceName)
}
// TestConfigPortRange tests port validation
func TestConfigPortRange(t *testing.T) {
testCases := []struct {
name string
port int
valid bool
expected int
}{
{"Standard HTTPS", 443, true, 443},
{"Custom Port", 8443, true, 8443},
{"High Port", 65535, true, 65535},
{"Port 80", 80, true, 80},
{"Port 8080", 8080, true, 8080},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cfg := &Config{
Port: tc.port,
}
assert.Equal(t, tc.expected, cfg.Port)
if tc.valid {
assert.True(t, cfg.Port > 0)
assert.True(t, cfg.Port <= 65535)
}
})
}
}
// TestConfigHostnames tests host configuration
func TestConfigHostnames(t *testing.T) {
testCases := []struct {
name string
host string
expected string
}{
{"Localhost", "127.0.0.1", "127.0.0.1"},
{"All Interfaces", "0.0.0.0", "0.0.0.0"},
{"Localhost Name", "localhost", "localhost"},
{"Custom Domain", "s3.example.com", "s3.example.com"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cfg := &Config{
Host: tc.host,
}
assert.Equal(t, tc.expected, cfg.Host)
assert.NotEmpty(t, cfg.Host)
})
}
}
// TestConfigBasePath tests base path configuration
func TestConfigBasePath(t *testing.T) {
testCases := []struct {
name string
basePath string
}{
{"Absolute Path", "/var/lib/predastore"},
{"Relative Path", "data/predastore"},
{"Temp Directory", "/tmp/predastore"},
{"User Directory", "/home/user/predastore"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cfg := &Config{
BasePath: tc.basePath,
}
assert.Equal(t, tc.basePath, cfg.BasePath)
})
}
}
// TestConfigTLSPaths tests TLS certificate and key paths
func TestConfigTLSPaths(t *testing.T) {
cfg := &Config{
TlsCert: "/etc/ssl/certs/server.crt",
TlsKey: "/etc/ssl/private/server.key",
}
assert.NotEmpty(t, cfg.TlsCert)
assert.NotEmpty(t, cfg.TlsKey)
assert.Contains(t, cfg.TlsCert, ".crt")
assert.Contains(t, cfg.TlsKey, ".key")
}
// TestConfigMinimal tests minimal required config
func TestConfigMinimal(t *testing.T) {
cfg := &Config{
ConfigPath: "/tmp/config.toml",
Port: 8443,
Host: "127.0.0.1",
}
svc, err := New(cfg)
assert.NoError(t, err)
assert.NotNil(t, svc)
// Minimal config should work
assert.NotEmpty(t, cfg.ConfigPath)
assert.True(t, cfg.Port > 0)
assert.NotEmpty(t, cfg.Host)
}
// TestServiceStartWithoutConfig tests Start method behavior
// Note: This will fail without proper config file, which is expected
func TestServiceStartWithoutConfig(t *testing.T) {
// Skip this test - it requires actual config file and will block/exit
// This is covered by integration tests instead
t.Skip("Skipping test that requires actual predastore config file - covered by integration tests")
}
// TestConfigDebugFlag tests debug flag behavior
func TestConfigDebugFlag(t *testing.T) {
// Debug enabled
cfg1 := &Config{
Debug: true,
}
assert.True(t, cfg1.Debug)
// Debug disabled
cfg2 := &Config{
Debug: false,
}
assert.False(t, cfg2.Debug)
// Default should be false
cfg3 := &Config{}
assert.False(t, cfg3.Debug)
}
// TestConfigValidation tests validation of required fields
func TestConfigValidation(t *testing.T) {
// Empty config - fields should be empty/zero
cfg := &Config{}
assert.Empty(t, cfg.ConfigPath)
assert.Equal(t, 0, cfg.Port)
assert.Empty(t, cfg.Host)
// Valid config
cfg2 := &Config{
ConfigPath: "/etc/predastore/config.toml",
Port: 8443,
Host: "0.0.0.0",
}
assert.NotEmpty(t, cfg2.ConfigPath)
assert.True(t, cfg2.Port > 0)
assert.NotEmpty(t, cfg2.Host)
}
// TestMultipleServiceInstances tests creating multiple service instances
func TestMultipleServiceInstances(t *testing.T) {
cfg1 := &Config{
ConfigPath: "/tmp/config1.toml",
Port: 8443,
Host: "127.0.0.1",
}
cfg2 := &Config{
ConfigPath: "/tmp/config2.toml",
Port: 9443,
Host: "127.0.0.1",
}
svc1, err := New(cfg1)
assert.NoError(t, err)
assert.NotNil(t, svc1)
svc2, err := New(cfg2)
assert.NoError(t, err)
assert.NotNil(t, svc2)
// Services should be independent
assert.NotEqual(t, svc1.Config.Port, svc2.Config.Port)
assert.NotEqual(t, svc1.Config.ConfigPath, svc2.Config.ConfigPath)
}
// TestConfigPointerPreservation tests that config pointer is preserved
func TestConfigPointerPreservation(t *testing.T) {
cfg := &Config{
Port: 8443,
Host: "127.0.0.1",
}
svc, err := New(cfg)
assert.NoError(t, err)
// Modify original config
cfg.Port = 9443
// Service should reflect the change (same pointer)
assert.Equal(t, 9443, svc.Config.Port)
}
@@ -46,15 +46,15 @@ func setupTestConfig(t *testing.T, natsURL string) *Config {
testDir := t.TempDir()
cfg := &Config{
NatsHost: natsURL,
S3Host: "https://s3.mock.local",
Bucket: "test-bucket",
Region: "us-east-1",
AccessKey: "test-access-key",
SecretKey: "test-secret-key",
BaseDir: testDir,
PluginPath: filepath.Join(testDir, "plugins"), // Use temp dir to avoid actual nbdkit execution
Debug: true,
NatsHost: natsURL,
S3Host: "https://s3.mock.local",
Bucket: "test-bucket",
Region: "us-east-1",
AccessKey: "test-access-key",
SecretKey: "test-secret-key",
BaseDir: testDir,
PluginPath: filepath.Join(testDir, "plugins"), // Use temp dir to avoid actual nbdkit execution
Debug: true,
MountedVolumes: []MountedVolume{},
}
@@ -248,6 +248,8 @@ func TestIntegration_EBSUnmountRequest(t *testing.T) {
// TestIntegration_EBSUnmountNonExistentVolume tests unmounting a volume that doesn't exist
func TestIntegration_EBSUnmountNonExistentVolume(t *testing.T) {
// Skip
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}