Breaking change. The monorepo's gitea host was previously inferred from the first target's subrepo_url, silently coupling the two and breaking on any multi-host setup (monorepo on host A, target on host B). v2.0.0 replaces the inference with a required josh.monorepo_url field that mirrors subrepo_url — same parsing, same SSH/HTTPS auth options, same shape. - schema_version: 2 is now required; v1 configs are rejected with a clear migration error - josh.monorepo_url is required; josh.monorepo_auth is optional (default https) - mono_auth_url() in lib/auth.sh mirrors subrepo_auth_url() - MONOREPO_API derives from monorepo_url's host instead of .[0].gitea_host; env-var override preserved as escape hatch - initial_import() and force-push call mono_auth_url() instead of building URLs inline - ADR-012 documents the rationale; CHANGELOG includes migration snippet Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
255 lines
6.7 KiB
Bash
255 lines
6.7 KiB
Bash
#!/usr/bin/env bats
|
|
# tests/unit/config.bats — Config parsing tests
|
|
|
|
setup() {
|
|
export JOSH_SYNC_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
|
|
source "$JOSH_SYNC_ROOT/lib/core.sh"
|
|
source "$JOSH_SYNC_ROOT/lib/config.sh"
|
|
|
|
FIXTURES="$JOSH_SYNC_ROOT/tests/fixtures"
|
|
}
|
|
|
|
@test "parse_config loads multi-target config" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/multi-target.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
[ "$JOSH_PROXY_URL" = "https://josh.test.local" ]
|
|
[ "$MONOREPO_PATH" = "org/monorepo" ]
|
|
[ "$BOT_NAME" = "test-bot" ]
|
|
[ "$BOT_EMAIL" = "test-bot@test.local" ]
|
|
[ "$BOT_TRAILER" = "Josh-Sync-Origin" ]
|
|
}
|
|
|
|
@test "parse_config derives gitea_host from HTTPS URL" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/multi-target.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
local host
|
|
host=$(echo "$JOSH_SYNC_TARGETS" | jq -r '.[0].gitea_host')
|
|
[ "$host" = "gitea.test.local" ]
|
|
}
|
|
|
|
@test "parse_config derives gitea_host from SSH URL" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/multi-target.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
local host
|
|
host=$(echo "$JOSH_SYNC_TARGETS" | jq -r '.[1].gitea_host')
|
|
[ "$host" = "gitea.test.local" ]
|
|
}
|
|
|
|
@test "parse_config derives subrepo_repo_path from HTTPS URL" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/multi-target.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
local path
|
|
path=$(echo "$JOSH_SYNC_TARGETS" | jq -r '.[0].subrepo_repo_path')
|
|
[ "$path" = "ext/app-a" ]
|
|
}
|
|
|
|
@test "parse_config derives subrepo_repo_path from SSH URL" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/multi-target.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
local path
|
|
path=$(echo "$JOSH_SYNC_TARGETS" | jq -r '.[1].subrepo_repo_path')
|
|
[ "$path" = "ext/app-b" ]
|
|
}
|
|
|
|
@test "parse_config auto-derives josh_filter from subfolder when not set" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/minimal.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
local filter
|
|
filter=$(echo "$JOSH_SYNC_TARGETS" | jq -r '.[0].josh_filter')
|
|
[ "$filter" = ":/services/example" ]
|
|
}
|
|
|
|
@test "parse_config preserves explicit josh_filter" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/multi-target.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
local filter
|
|
filter=$(echo "$JOSH_SYNC_TARGETS" | jq -r '.[0].josh_filter')
|
|
[ "$filter" = ":/services/app-a" ]
|
|
}
|
|
|
|
@test "parse_config reports correct target count" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/multi-target.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
local count
|
|
count=$(echo "$JOSH_SYNC_TARGETS" | jq 'length')
|
|
[ "$count" -eq 2 ]
|
|
}
|
|
|
|
@test "parse_config fails on missing config file" {
|
|
cd "$(mktemp -d)"
|
|
run bash -c 'source "$JOSH_SYNC_ROOT/lib/core.sh"; source "$JOSH_SYNC_ROOT/lib/config.sh"; parse_config "nonexistent.yml"'
|
|
[ "$status" -ne 0 ]
|
|
}
|
|
|
|
@test "parse_config fails when schema_version is missing (no v1 fallback)" {
|
|
cd "$(mktemp -d)"
|
|
cat > .josh-sync.yml <<'YAML'
|
|
josh:
|
|
proxy_url: "https://josh.test.local"
|
|
monorepo_path: "org/repo"
|
|
monorepo_url: "git@gitea.test.local:org/repo.git"
|
|
targets:
|
|
- name: "x"
|
|
subfolder: "x"
|
|
subrepo_url: "git@gitea.test.local:ext/x.git"
|
|
branches: { main: main }
|
|
bot:
|
|
name: "b"
|
|
email: "b@b"
|
|
trailer: "T"
|
|
YAML
|
|
run bash -c 'source "$JOSH_SYNC_ROOT/lib/core.sh"; source "$JOSH_SYNC_ROOT/lib/config.sh"; parse_config ".josh-sync.yml"'
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"schema_version missing"* ]]
|
|
}
|
|
|
|
@test "parse_config rejects schema_version: 1" {
|
|
cd "$(mktemp -d)"
|
|
cat > .josh-sync.yml <<'YAML'
|
|
schema_version: 1
|
|
josh:
|
|
proxy_url: "https://josh.test.local"
|
|
monorepo_path: "org/repo"
|
|
monorepo_url: "git@gitea.test.local:org/repo.git"
|
|
targets:
|
|
- name: "x"
|
|
subfolder: "x"
|
|
subrepo_url: "git@gitea.test.local:ext/x.git"
|
|
branches: { main: main }
|
|
bot:
|
|
name: "b"
|
|
email: "b@b"
|
|
trailer: "T"
|
|
YAML
|
|
run bash -c 'source "$JOSH_SYNC_ROOT/lib/core.sh"; source "$JOSH_SYNC_ROOT/lib/config.sh"; parse_config ".josh-sync.yml"'
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"Unsupported schema_version"* ]] || [[ "$output" == *"requires schema_version 2"* ]]
|
|
}
|
|
|
|
@test "parse_config fails when josh.monorepo_url is missing" {
|
|
cd "$(mktemp -d)"
|
|
cat > .josh-sync.yml <<'YAML'
|
|
schema_version: 2
|
|
josh:
|
|
proxy_url: "https://josh.test.local"
|
|
monorepo_path: "org/repo"
|
|
targets:
|
|
- name: "x"
|
|
subfolder: "x"
|
|
subrepo_url: "git@gitea.test.local:ext/x.git"
|
|
branches: { main: main }
|
|
bot:
|
|
name: "b"
|
|
email: "b@b"
|
|
trailer: "T"
|
|
YAML
|
|
run bash -c 'source "$JOSH_SYNC_ROOT/lib/core.sh"; source "$JOSH_SYNC_ROOT/lib/config.sh"; parse_config ".josh-sync.yml"'
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"monorepo_url"* ]]
|
|
}
|
|
|
|
@test "parse_config exports MONOREPO_URL, MONOREPO_AUTH, MONOREPO_GITEA_HOST, MONOREPO_REPO_PATH from SSH form" {
|
|
cd "$(mktemp -d)"
|
|
cp "$FIXTURES/minimal.yml" .josh-sync.yml
|
|
|
|
parse_config ".josh-sync.yml"
|
|
|
|
[ "$MONOREPO_URL" = "git@gitea.test.local:org/repo.git" ]
|
|
[ "$MONOREPO_AUTH" = "https" ]
|
|
[ "$MONOREPO_GITEA_HOST" = "gitea.test.local" ]
|
|
[ "$MONOREPO_REPO_PATH" = "org/repo" ]
|
|
}
|
|
|
|
@test "parse_config derives MONOREPO_GITEA_HOST from HTTPS monorepo_url" {
|
|
cd "$(mktemp -d)"
|
|
cat > .josh-sync.yml <<'YAML'
|
|
schema_version: 2
|
|
josh:
|
|
proxy_url: "https://josh.test.local"
|
|
monorepo_path: "org/repo"
|
|
monorepo_url: "https://gitea.example.io/org/repo.git"
|
|
targets:
|
|
- name: "x"
|
|
subfolder: "x"
|
|
subrepo_url: "git@gitea.test.local:ext/x.git"
|
|
branches: { main: main }
|
|
bot:
|
|
name: "b"
|
|
email: "b@b"
|
|
trailer: "T"
|
|
YAML
|
|
parse_config ".josh-sync.yml"
|
|
[ "$MONOREPO_GITEA_HOST" = "gitea.example.io" ]
|
|
[ "$MONOREPO_REPO_PATH" = "org/repo" ]
|
|
}
|
|
|
|
@test "parse_config derives MONOREPO_API from monorepo_url's host (cross-host: monorepo and target on different hosts)" {
|
|
cd "$(mktemp -d)"
|
|
cat > .josh-sync.yml <<'YAML'
|
|
schema_version: 2
|
|
josh:
|
|
proxy_url: "https://josh.example.io"
|
|
monorepo_path: "org/repo"
|
|
monorepo_url: "git@code.example.io:org/repo.git"
|
|
targets:
|
|
- name: "x"
|
|
subfolder: "x"
|
|
subrepo_url: "git@other-host.io:ext/x.git"
|
|
branches: { main: main }
|
|
bot:
|
|
name: "b"
|
|
email: "b@b"
|
|
trailer: "T"
|
|
YAML
|
|
parse_config ".josh-sync.yml"
|
|
[ "$MONOREPO_API" = "https://code.example.io/api/v1/repos/org/repo" ]
|
|
}
|
|
|
|
@test "parse_config rejects invalid monorepo_auth value" {
|
|
cd "$(mktemp -d)"
|
|
cat > .josh-sync.yml <<'YAML'
|
|
schema_version: 2
|
|
josh:
|
|
proxy_url: "https://josh.test.local"
|
|
monorepo_path: "org/repo"
|
|
monorepo_url: "git@gitea.test.local:org/repo.git"
|
|
monorepo_auth: "weird"
|
|
targets:
|
|
- name: "x"
|
|
subfolder: "x"
|
|
subrepo_url: "git@gitea.test.local:ext/x.git"
|
|
branches: { main: main }
|
|
bot:
|
|
name: "b"
|
|
email: "b@b"
|
|
trailer: "T"
|
|
YAML
|
|
run bash -c 'source "$JOSH_SYNC_ROOT/lib/core.sh"; source "$JOSH_SYNC_ROOT/lib/config.sh"; parse_config ".josh-sync.yml"'
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"monorepo_auth"* ]]
|
|
}
|