cli/internal/cmd/php/coolify_test.go

503 lines
16 KiB
Go
Raw Normal View History

package php
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCoolifyClient_Good(t *testing.T) {
t.Run("creates client with correct base URL", func(t *testing.T) {
client := NewCoolifyClient("https://coolify.example.com", "token")
assert.Equal(t, "https://coolify.example.com", client.BaseURL)
assert.Equal(t, "token", client.Token)
assert.NotNil(t, client.HTTPClient)
})
t.Run("strips trailing slash from base URL", func(t *testing.T) {
client := NewCoolifyClient("https://coolify.example.com/", "token")
assert.Equal(t, "https://coolify.example.com", client.BaseURL)
})
t.Run("http client has timeout", func(t *testing.T) {
client := NewCoolifyClient("https://coolify.example.com", "token")
assert.Equal(t, 30*time.Second, client.HTTPClient.Timeout)
})
}
func TestCoolifyConfig_Good(t *testing.T) {
t.Run("all fields accessible", func(t *testing.T) {
config := CoolifyConfig{
URL: "https://coolify.example.com",
Token: "secret-token",
AppID: "app-123",
StagingAppID: "staging-456",
}
assert.Equal(t, "https://coolify.example.com", config.URL)
assert.Equal(t, "secret-token", config.Token)
assert.Equal(t, "app-123", config.AppID)
assert.Equal(t, "staging-456", config.StagingAppID)
})
}
func TestCoolifyDeployment_Good(t *testing.T) {
t.Run("all fields accessible", func(t *testing.T) {
now := time.Now()
deployment := CoolifyDeployment{
ID: "dep-123",
Status: "finished",
CommitSHA: "abc123",
CommitMsg: "Test commit",
Branch: "main",
CreatedAt: now,
FinishedAt: now.Add(5 * time.Minute),
Log: "Build successful",
DeployedURL: "https://app.example.com",
}
assert.Equal(t, "dep-123", deployment.ID)
assert.Equal(t, "finished", deployment.Status)
assert.Equal(t, "abc123", deployment.CommitSHA)
assert.Equal(t, "Test commit", deployment.CommitMsg)
assert.Equal(t, "main", deployment.Branch)
})
}
func TestCoolifyApp_Good(t *testing.T) {
t.Run("all fields accessible", func(t *testing.T) {
app := CoolifyApp{
ID: "app-123",
Name: "MyApp",
FQDN: "https://myapp.example.com",
Status: "running",
Repository: "https://github.com/user/repo",
Branch: "main",
Environment: "production",
}
assert.Equal(t, "app-123", app.ID)
assert.Equal(t, "MyApp", app.Name)
assert.Equal(t, "https://myapp.example.com", app.FQDN)
assert.Equal(t, "running", app.Status)
})
}
func TestLoadCoolifyConfigFromFile_Good(t *testing.T) {
t.Run("loads config from .env file", func(t *testing.T) {
dir := t.TempDir()
envContent := `COOLIFY_URL=https://coolify.example.com
COOLIFY_TOKEN=secret-token
COOLIFY_APP_ID=app-123
COOLIFY_STAGING_APP_ID=staging-456`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
config, err := LoadCoolifyConfigFromFile(filepath.Join(dir, ".env"))
assert.NoError(t, err)
assert.Equal(t, "https://coolify.example.com", config.URL)
assert.Equal(t, "secret-token", config.Token)
assert.Equal(t, "app-123", config.AppID)
assert.Equal(t, "staging-456", config.StagingAppID)
})
t.Run("handles quoted values", func(t *testing.T) {
dir := t.TempDir()
envContent := `COOLIFY_URL="https://coolify.example.com"
COOLIFY_TOKEN='secret-token'`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
config, err := LoadCoolifyConfigFromFile(filepath.Join(dir, ".env"))
assert.NoError(t, err)
assert.Equal(t, "https://coolify.example.com", config.URL)
assert.Equal(t, "secret-token", config.Token)
})
t.Run("ignores comments", func(t *testing.T) {
dir := t.TempDir()
envContent := `# This is a comment
COOLIFY_URL=https://coolify.example.com
# COOLIFY_TOKEN=wrong-token
COOLIFY_TOKEN=correct-token`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
config, err := LoadCoolifyConfigFromFile(filepath.Join(dir, ".env"))
assert.NoError(t, err)
assert.Equal(t, "correct-token", config.Token)
})
t.Run("ignores blank lines", func(t *testing.T) {
dir := t.TempDir()
envContent := `COOLIFY_URL=https://coolify.example.com
COOLIFY_TOKEN=secret-token`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
config, err := LoadCoolifyConfigFromFile(filepath.Join(dir, ".env"))
assert.NoError(t, err)
assert.Equal(t, "https://coolify.example.com", config.URL)
})
}
func TestLoadCoolifyConfigFromFile_Bad(t *testing.T) {
t.Run("fails when COOLIFY_URL missing", func(t *testing.T) {
dir := t.TempDir()
envContent := `COOLIFY_TOKEN=secret-token`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
_, err = LoadCoolifyConfigFromFile(filepath.Join(dir, ".env"))
assert.Error(t, err)
assert.Contains(t, err.Error(), "COOLIFY_URL is not set")
})
t.Run("fails when COOLIFY_TOKEN missing", func(t *testing.T) {
dir := t.TempDir()
envContent := `COOLIFY_URL=https://coolify.example.com`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
_, err = LoadCoolifyConfigFromFile(filepath.Join(dir, ".env"))
assert.Error(t, err)
assert.Contains(t, err.Error(), "COOLIFY_TOKEN is not set")
})
}
func TestLoadCoolifyConfig_FromDirectory_Good(t *testing.T) {
t.Run("loads from directory", func(t *testing.T) {
dir := t.TempDir()
envContent := `COOLIFY_URL=https://coolify.example.com
COOLIFY_TOKEN=secret-token`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
config, err := LoadCoolifyConfig(dir)
assert.NoError(t, err)
assert.Equal(t, "https://coolify.example.com", config.URL)
})
}
func TestValidateCoolifyConfig_Bad(t *testing.T) {
t.Run("returns error for empty URL", func(t *testing.T) {
config := &CoolifyConfig{Token: "token"}
_, err := validateCoolifyConfig(config)
assert.Error(t, err)
assert.Contains(t, err.Error(), "COOLIFY_URL is not set")
})
t.Run("returns error for empty token", func(t *testing.T) {
config := &CoolifyConfig{URL: "https://coolify.example.com"}
_, err := validateCoolifyConfig(config)
assert.Error(t, err)
assert.Contains(t, err.Error(), "COOLIFY_TOKEN is not set")
})
}
func TestCoolifyClient_TriggerDeploy_Good(t *testing.T) {
t.Run("triggers deployment successfully", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v1/applications/app-123/deploy", r.URL.Path)
assert.Equal(t, "POST", r.Method)
assert.Equal(t, "Bearer secret-token", r.Header.Get("Authorization"))
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
resp := CoolifyDeployment{
ID: "dep-456",
Status: "queued",
CreatedAt: time.Now(),
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
deployment, err := client.TriggerDeploy(context.Background(), "app-123", false)
assert.NoError(t, err)
assert.Equal(t, "dep-456", deployment.ID)
assert.Equal(t, "queued", deployment.Status)
})
t.Run("triggers deployment with force", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var body map[string]interface{}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewDecoder(r.Body).Decode(&body)
assert.Equal(t, true, body["force"])
resp := CoolifyDeployment{ID: "dep-456", Status: "queued"}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
_, err := client.TriggerDeploy(context.Background(), "app-123", true)
assert.NoError(t, err)
})
t.Run("handles minimal response", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Return an invalid JSON response to trigger the fallback
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_, _ = w.Write([]byte("not json"))
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
deployment, err := client.TriggerDeploy(context.Background(), "app-123", false)
assert.NoError(t, err)
// The fallback response should be returned
assert.Equal(t, "queued", deployment.Status)
})
}
func TestCoolifyClient_TriggerDeploy_Bad(t *testing.T) {
t.Run("fails on HTTP error", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(map[string]string{"message": "Internal error"})
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
_, err := client.TriggerDeploy(context.Background(), "app-123", false)
assert.Error(t, err)
assert.Contains(t, err.Error(), "API error")
})
}
func TestCoolifyClient_GetDeployment_Good(t *testing.T) {
t.Run("gets deployment details", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v1/applications/app-123/deployments/dep-456", r.URL.Path)
assert.Equal(t, "GET", r.Method)
resp := CoolifyDeployment{
ID: "dep-456",
Status: "finished",
CommitSHA: "abc123",
Branch: "main",
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
deployment, err := client.GetDeployment(context.Background(), "app-123", "dep-456")
assert.NoError(t, err)
assert.Equal(t, "dep-456", deployment.ID)
assert.Equal(t, "finished", deployment.Status)
assert.Equal(t, "abc123", deployment.CommitSHA)
})
}
func TestCoolifyClient_GetDeployment_Bad(t *testing.T) {
t.Run("fails on 404", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(map[string]string{"error": "Not found"})
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
_, err := client.GetDeployment(context.Background(), "app-123", "dep-456")
assert.Error(t, err)
assert.Contains(t, err.Error(), "Not found")
})
}
func TestCoolifyClient_ListDeployments_Good(t *testing.T) {
t.Run("lists deployments", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v1/applications/app-123/deployments", r.URL.Path)
assert.Equal(t, "10", r.URL.Query().Get("limit"))
resp := []CoolifyDeployment{
{ID: "dep-1", Status: "finished"},
{ID: "dep-2", Status: "failed"},
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
deployments, err := client.ListDeployments(context.Background(), "app-123", 10)
assert.NoError(t, err)
assert.Len(t, deployments, 2)
assert.Equal(t, "dep-1", deployments[0].ID)
assert.Equal(t, "dep-2", deployments[1].ID)
})
t.Run("lists without limit", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "", r.URL.Query().Get("limit"))
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode([]CoolifyDeployment{})
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
_, err := client.ListDeployments(context.Background(), "app-123", 0)
assert.NoError(t, err)
})
}
func TestCoolifyClient_Rollback_Good(t *testing.T) {
t.Run("triggers rollback", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v1/applications/app-123/rollback", r.URL.Path)
assert.Equal(t, "POST", r.Method)
var body map[string]string
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewDecoder(r.Body).Decode(&body)
assert.Equal(t, "dep-old", body["deployment_id"])
resp := CoolifyDeployment{
ID: "dep-new",
Status: "rolling_back",
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
deployment, err := client.Rollback(context.Background(), "app-123", "dep-old")
assert.NoError(t, err)
assert.Equal(t, "dep-new", deployment.ID)
assert.Equal(t, "rolling_back", deployment.Status)
})
}
func TestCoolifyClient_GetApp_Good(t *testing.T) {
t.Run("gets app details", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v1/applications/app-123", r.URL.Path)
assert.Equal(t, "GET", r.Method)
resp := CoolifyApp{
ID: "app-123",
Name: "MyApp",
FQDN: "https://myapp.example.com",
Status: "running",
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "secret-token")
app, err := client.GetApp(context.Background(), "app-123")
assert.NoError(t, err)
assert.Equal(t, "app-123", app.ID)
assert.Equal(t, "MyApp", app.Name)
assert.Equal(t, "https://myapp.example.com", app.FQDN)
})
}
func TestCoolifyClient_SetHeaders(t *testing.T) {
t.Run("sets all required headers", func(t *testing.T) {
client := NewCoolifyClient("https://coolify.example.com", "my-token")
req, _ := http.NewRequest("GET", "https://coolify.example.com", nil)
client.setHeaders(req)
assert.Equal(t, "Bearer my-token", req.Header.Get("Authorization"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
assert.Equal(t, "application/json", req.Header.Get("Accept"))
})
}
func TestCoolifyClient_ParseError(t *testing.T) {
t.Run("parses message field", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(map[string]string{"message": "Bad request message"})
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "token")
_, err := client.GetApp(context.Background(), "app-123")
assert.Error(t, err)
assert.Contains(t, err.Error(), "Bad request message")
})
t.Run("parses error field", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = json.NewEncoder(w).Encode(map[string]string{"error": "Error message"})
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "token")
_, err := client.GetApp(context.Background(), "app-123")
assert.Error(t, err)
assert.Contains(t, err.Error(), "Error message")
})
t.Run("returns raw body when no JSON fields", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_, _ = w.Write([]byte("Raw error message"))
}))
defer server.Close()
client := NewCoolifyClient(server.URL, "token")
_, err := client.GetApp(context.Background(), "app-123")
assert.Error(t, err)
assert.Contains(t, err.Error(), "Raw error message")
})
}
func TestEnvironmentVariablePriority(t *testing.T) {
t.Run("env vars take precedence over .env file", func(t *testing.T) {
dir := t.TempDir()
envContent := `COOLIFY_URL=https://from-file.com
COOLIFY_TOKEN=file-token`
err := os.WriteFile(filepath.Join(dir, ".env"), []byte(envContent), 0644)
require.NoError(t, err)
// Set environment variables
origURL := os.Getenv("COOLIFY_URL")
origToken := os.Getenv("COOLIFY_TOKEN")
defer func() {
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = os.Setenv("COOLIFY_URL", origURL)
_ = os.Setenv("COOLIFY_TOKEN", origToken)
}()
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = os.Setenv("COOLIFY_URL", "https://from-env.com")
_ = os.Setenv("COOLIFY_TOKEN", "env-token")
config, err := LoadCoolifyConfig(dir)
assert.NoError(t, err)
// Environment variables should take precedence
assert.Equal(t, "https://from-env.com", config.URL)
assert.Equal(t, "env-token", config.Token)
})
}