A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
at main 291 lines 8.4 kB view raw
1package hold 2 3import ( 4 "os" 5 "path/filepath" 6 "strings" 7 "testing" 8 "time" 9) 10 11func init() { 12 // Point metadata endpoint to a closed listener so it fails instantly instead of 13 // waiting 2s for the real 169.254.169.254 to timeout on non-cloud machines. 14 metadataEndpoint = "http://127.0.0.1:1" 15} 16 17// setupEnv sets environment variables for testing and returns a cleanup function 18func setupEnv(t *testing.T, vars map[string]string) func() { 19 // Save original env 20 original := make(map[string]string) 21 for k := range vars { 22 original[k] = os.Getenv(k) 23 } 24 25 // Set test env vars 26 for k, v := range vars { 27 if err := os.Setenv(k, v); err != nil { 28 t.Fatalf("Failed to set env %s: %v", k, err) 29 } 30 } 31 32 // Return cleanup function 33 return func() { 34 for k, v := range original { 35 if v == "" { 36 os.Unsetenv(k) 37 } else { 38 os.Setenv(k, v) 39 } 40 } 41 } 42} 43 44func TestLoadConfig_Success(t *testing.T) { 45 cleanup := setupEnv(t, map[string]string{ 46 "HOLD_SERVER_PUBLIC_URL": "https://hold.example.com", 47 "HOLD_SERVER_ADDR": ":9000", 48 "HOLD_SERVER_PUBLIC": "true", 49 "HOLD_SERVER_TEST_MODE": "true", 50 "HOLD_REGISTRATION_OWNER_DID": "did:plc:owner123", 51 "HOLD_REGISTRATION_ALLOW_ALL_CREW": "true", 52 "S3_BUCKET": "test-bucket", 53 "AWS_ACCESS_KEY_ID": "test-key", 54 "AWS_SECRET_ACCESS_KEY": "test-secret", 55 "HOLD_DATABASE_PATH": "/tmp/test-db", 56 "HOLD_DATABASE_KEY_PATH": "/tmp/test-key.pem", 57 }) 58 defer cleanup() 59 60 cfg, err := LoadConfig("") 61 if err != nil { 62 t.Fatalf("Expected success, got error: %v", err) 63 } 64 65 // Verify server config 66 if cfg.Server.PublicURL != "https://hold.example.com" { 67 t.Errorf("Expected PublicURL=https://hold.example.com, got %s", cfg.Server.PublicURL) 68 } 69 if cfg.Server.Addr != ":9000" { 70 t.Errorf("Expected Addr=:9000, got %s", cfg.Server.Addr) 71 } 72 if !cfg.Server.Public { 73 t.Error("Expected Public=true") 74 } 75 if !cfg.Server.TestMode { 76 t.Error("Expected TestMode=true") 77 } 78 if cfg.Server.ReadTimeout != 5*time.Minute { 79 t.Errorf("Expected ReadTimeout=5m, got %v", cfg.Server.ReadTimeout) 80 } 81 82 // Verify registration config 83 if cfg.Registration.OwnerDID != "did:plc:owner123" { 84 t.Errorf("Expected OwnerDID=did:plc:owner123, got %s", cfg.Registration.OwnerDID) 85 } 86 if !cfg.Registration.AllowAllCrew { 87 t.Error("Expected AllowAllCrew=true") 88 } 89 90 // Verify database config 91 if cfg.Database.Path != "/tmp/test-db" { 92 t.Errorf("Expected Database.Path=/tmp/test-db, got %s", cfg.Database.Path) 93 } 94 if cfg.Database.KeyPath != "/tmp/test-key.pem" { 95 t.Errorf("Expected Database.KeyPath=/tmp/test-key.pem, got %s", cfg.Database.KeyPath) 96 } 97} 98 99func TestLoadConfig_MissingPublicURL(t *testing.T) { 100 cleanup := setupEnv(t, map[string]string{ 101 "HOLD_SERVER_PUBLIC_URL": "", // Missing required field 102 "S3_BUCKET": "test-bucket", 103 }) 104 defer cleanup() 105 106 _, err := LoadConfig("") 107 if err == nil { 108 t.Error("Expected error for missing HOLD_SERVER_PUBLIC_URL") 109 } 110} 111 112func TestLoadConfig_MissingS3Bucket(t *testing.T) { 113 cleanup := setupEnv(t, map[string]string{ 114 "HOLD_SERVER_PUBLIC_URL": "https://hold.example.com", 115 "S3_BUCKET": "", // Missing required field 116 }) 117 defer cleanup() 118 119 _, err := LoadConfig("") 120 if err == nil { 121 t.Error("Expected error for missing S3_BUCKET") 122 } 123} 124 125func TestLoadConfig_Defaults(t *testing.T) { 126 cleanup := setupEnv(t, map[string]string{ 127 "HOLD_SERVER_PUBLIC_URL": "https://hold.example.com", 128 "S3_BUCKET": "test-bucket", 129 "AWS_ACCESS_KEY_ID": "test-key", 130 "AWS_SECRET_ACCESS_KEY": "test-secret", 131 // Don't set optional vars - test defaults 132 "HOLD_SERVER_ADDR": "", 133 "HOLD_SERVER_PUBLIC": "", 134 "HOLD_SERVER_TEST_MODE": "", 135 "HOLD_REGISTRATION_OWNER_DID": "", 136 "HOLD_REGISTRATION_ALLOW_ALL_CREW": "", 137 "AWS_REGION": "", 138 "HOLD_DATABASE_PATH": "", 139 }) 140 defer cleanup() 141 142 cfg, err := LoadConfig("") 143 if err != nil { 144 t.Fatalf("Expected success, got error: %v", err) 145 } 146 147 // Verify defaults 148 if cfg.Server.Addr != ":8080" { 149 t.Errorf("Expected default Addr=:8080, got %s", cfg.Server.Addr) 150 } 151 if cfg.Server.Public { 152 t.Error("Expected default Public=false") 153 } 154 if cfg.Server.TestMode { 155 t.Error("Expected default TestMode=false") 156 } 157 if cfg.Registration.OwnerDID != "" { 158 t.Error("Expected default OwnerDID to be empty") 159 } 160 if cfg.Registration.AllowAllCrew { 161 t.Error("Expected default AllowAllCrew=false") 162 } 163 if cfg.Database.Path != "/var/lib/atcr-hold" { 164 t.Errorf("Expected default Database.Path=/var/lib/atcr-hold, got %s", cfg.Database.Path) 165 } 166} 167 168func TestLoadConfig_KeyPathDefault(t *testing.T) { 169 cleanup := setupEnv(t, map[string]string{ 170 "HOLD_SERVER_PUBLIC_URL": "https://hold.example.com", 171 "S3_BUCKET": "test-bucket", 172 "AWS_ACCESS_KEY_ID": "test-key", 173 "AWS_SECRET_ACCESS_KEY": "test-secret", 174 "HOLD_DATABASE_PATH": "/custom/db/path", 175 "HOLD_DATABASE_KEY_PATH": "", // Should default to {Database.Path}/signing.key 176 }) 177 defer cleanup() 178 179 cfg, err := LoadConfig("") 180 if err != nil { 181 t.Fatalf("Expected success, got error: %v", err) 182 } 183 184 expectedKeyPath := filepath.Join("/custom/db/path", "signing.key") 185 if cfg.Database.KeyPath != expectedKeyPath { 186 t.Errorf("Expected KeyPath=%s, got %s", expectedKeyPath, cfg.Database.KeyPath) 187 } 188} 189 190func TestS3Params_Complete(t *testing.T) { 191 sc := StorageConfig{ 192 AccessKey: "test-access-key", 193 SecretKey: "test-secret-key", 194 Region: "us-west-2", 195 Bucket: "test-bucket", 196 Endpoint: "https://s3.example.com", 197 } 198 199 params := sc.S3Params() 200 201 if params["accesskey"] != "test-access-key" { 202 t.Errorf("Expected accesskey=test-access-key, got %v", params["accesskey"]) 203 } 204 if params["secretkey"] != "test-secret-key" { 205 t.Errorf("Expected secretkey=test-secret-key, got %v", params["secretkey"]) 206 } 207 if params["region"] != "us-west-2" { 208 t.Errorf("Expected region=us-west-2, got %v", params["region"]) 209 } 210 if params["bucket"] != "test-bucket" { 211 t.Errorf("Expected bucket=test-bucket, got %v", params["bucket"]) 212 } 213 if params["regionendpoint"] != "https://s3.example.com" { 214 t.Errorf("Expected regionendpoint=https://s3.example.com, got %v", params["regionendpoint"]) 215 } 216} 217 218func TestS3Params_NoEndpoint(t *testing.T) { 219 sc := StorageConfig{ 220 AccessKey: "test-key", 221 SecretKey: "test-secret", 222 Region: "us-east-1", 223 Bucket: "test-bucket", 224 Endpoint: "", // No custom endpoint 225 } 226 227 params := sc.S3Params() 228 229 // Should have default region 230 if params["region"] != "us-east-1" { 231 t.Errorf("Expected default region=us-east-1, got %v", params["region"]) 232 } 233 234 // Should not have regionendpoint 235 if _, exists := params["regionendpoint"]; exists { 236 t.Error("Expected no regionendpoint when Endpoint not set") 237 } 238} 239 240func TestDefaultConfig_Hold(t *testing.T) { 241 cfg := DefaultConfig() 242 243 if cfg.Version != "0.1" { 244 t.Errorf("DefaultConfig().Version = %q, want \"0.1\"", cfg.Version) 245 } 246 if cfg.LogLevel != "info" { 247 t.Errorf("DefaultConfig().LogLevel = %q, want \"info\"", cfg.LogLevel) 248 } 249 if cfg.Server.Addr != ":8080" { 250 t.Errorf("DefaultConfig().Server.Addr = %q, want \":8080\"", cfg.Server.Addr) 251 } 252 if cfg.Storage.Region != "us-east-1" { 253 t.Errorf("DefaultConfig().Storage.Region = %q, want \"us-east-1\"", cfg.Storage.Region) 254 } 255 if cfg.Database.Path != "/var/lib/atcr-hold" { 256 t.Errorf("DefaultConfig().Database.Path = %q, want \"/var/lib/atcr-hold\"", cfg.Database.Path) 257 } 258 if cfg.Server.ReadTimeout != 5*time.Minute { 259 t.Errorf("DefaultConfig().Server.ReadTimeout = %v, want 5m", cfg.Server.ReadTimeout) 260 } 261} 262 263func TestExampleYAML_Hold(t *testing.T) { 264 out, err := ExampleYAML() 265 if err != nil { 266 t.Fatalf("ExampleYAML() error: %v", err) 267 } 268 269 s := string(out) 270 271 // Should contain the title 272 if !strings.Contains(s, "ATCR Hold Service Configuration") { 273 t.Error("expected title in YAML output") 274 } 275 276 // Should contain key fields with defaults 277 if !strings.Contains(s, "addr:") { 278 t.Error("expected addr field in YAML output") 279 } 280 if !strings.Contains(s, "bucket:") { 281 t.Error("expected bucket field in YAML output") 282 } 283 284 // Should contain comments 285 if !strings.Contains(s, "# Listen address") { 286 t.Error("expected comment for addr field") 287 } 288 if !strings.Contains(s, "# S3 bucket") { 289 t.Error("expected comment for bucket field") 290 } 291}