From c382f377a266745d119d370fc8c4aeb440efe06d Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:51:58 +0000 Subject: [PATCH 1/6] feat(ncdd): add focused app test commands Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- bin/ncdd | 11 +++++ lib/ncdd-test.sh | 110 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 lib/ncdd-test.sh diff --git a/bin/ncdd b/bin/ncdd index ee9435c..5604a6f 100644 --- a/bin/ncdd +++ b/bin/ncdd @@ -18,6 +18,8 @@ Commands: shell [--as USER] Open a shell in the app container exec [--as USER] -- COMMAND... Execute a command in the app container + test [--list|SUITE] + Run a focused app test suite help [ai] Show help or the compact agent contract EOF } @@ -30,6 +32,7 @@ NCDD agent contract - Git checkout/authentication stays on the host or CI workspace. - App worktrees belong under volumes/nextcloud/apps-extra/. - Use --as runtime for commands that must run as the Nextcloud runtime user. +- Use bin/ncdd test for supported app test suites instead of rebuilding container commands. EOF } @@ -127,6 +130,11 @@ if [ "$#" -gt 0 ]; then shift fi +if [ -f "$NCDD_ROOT/lib/ncdd-test.sh" ]; then + # shellcheck source=../lib/ncdd-test.sh + source "$NCDD_ROOT/lib/ncdd-test.sh" +fi + case "$command" in help) if [ "${1:-}" = "ai" ]; then @@ -181,6 +189,9 @@ case "$command" in doctor) doctor "$@" ;; + test) + ncdd_test "$@" + ;; *) fail "unknown command: $command" ;; diff --git a/lib/ncdd-test.sh b/lib/ncdd-test.sh new file mode 100644 index 0000000..25dce90 --- /dev/null +++ b/lib/ncdd-test.sh @@ -0,0 +1,110 @@ +# shellcheck shell=bash + +ncdd_test_usage() { + cat <<'EOF' +Usage: + ncdd test --list + ncdd test phpunit --app APP [-- PHPUnit arguments...] + ncdd test behat --app APP [-- Behat arguments...] + +The app must already exist under /var/www/html/apps-extra/ in the existing +NCDD stack. NCDD does not clone or install application source code. +EOF +} + +ncdd_test_list() { + printf '%s\n' phpunit behat +} + +ncdd_test_validate_app() { + local app=$1 + + if [[ ! "$app" =~ ^[A-Za-z0-9._-]+$ ]]; then + fail "invalid app id: $app" + fi +} + +ncdd_test_parse_app() { + local app='' + + while [ "$#" -gt 0 ]; do + case "$1" in + --app) + [ "$#" -ge 2 ] || fail "--app requires a value" + app=$2 + shift 2 + ;; + --) + shift + break + ;; + *) + break + ;; + esac + done + + if [ -z "$app" ]; then + app=${NCDD_TEST_APP:-} + fi + [ -n "$app" ] || fail "test suite requires --app APP or NCDD_TEST_APP" + + ncdd_test_validate_app "$app" + + NCDD_TEST_RESOLVED_APP=$app + NCDD_TEST_REMAINING_ARGS=("$@") +} + +ncdd_test_phpunit() { + ncdd_test_parse_app "$@" + + local app_dir="/var/www/html/apps-extra/$NCDD_TEST_RESOLVED_APP" + local user=${NCDD_PHPUNIT_USER:-root} + + compose exec -T \ + --user "$user" \ + --workdir "$app_dir" \ + "$APP_SERVICE" \ + vendor/bin/phpunit \ + "${NCDD_TEST_REMAINING_ARGS[@]}" +} + +ncdd_test_behat() { + ncdd_test_parse_app "$@" + + local app_dir="/var/www/html/apps-extra/$NCDD_TEST_RESOLVED_APP" + local integration_dir="$app_dir/${NCDD_BEHAT_DIR:-tests/integration}" + + compose exec -T \ + --user "$RUNTIME_USER" \ + --workdir "$integration_dir" \ + -e BEHAT_ROOT_DIR=/var/www/html \ + -e "BEHAT_RUN_AS=$RUNTIME_USER" \ + -e "BEHAT_VERBOSE=${BEHAT_VERBOSE:-1}" \ + "$APP_SERVICE" \ + vendor/bin/behat \ + "${NCDD_TEST_REMAINING_ARGS[@]}" +} + +ncdd_test() { + case "${1:-}" in + --list) + [ "$#" -eq 1 ] || fail "test --list does not accept extra arguments" + ncdd_test_list + ;; + -h|--help|'') + ncdd_test_usage + ;; + phpunit) + shift + ncdd_test_phpunit "$@" + ;; + behat) + shift + ncdd_test_behat "$@" + ;; + *) + fail "unknown test suite: $1" + ;; + esac +} From a2afc7b324a903ef6caf8b4338b2760a82440f61 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:52:22 +0000 Subject: [PATCH 2/6] test(ncdd): cover focused test command contract Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- docs/ncdd.md | 20 ++++++++++++++++++-- tests/ncdd.bats | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/docs/ncdd.md b/docs/ncdd.md index 112536f..1b2c3db 100644 --- a/docs/ncdd.md +++ b/docs/ncdd.md @@ -54,8 +54,24 @@ The CLI is Bash, so its behavior is covered with Bats rather than ad-hoc shell a make test-ncdd ``` -The GitHub workflow pins the Bats setup action to an immutable commit SHA and pins the Bats version explicitly. +The GitHub workflow pins the Bats setup action to an immutable commit SHA. The action selects its current default Bats version, avoiding a second version pin that would need separate maintenance. + +## Focused app tests + +NCDD exposes a small test API for test runners that require the existing Nextcloud runtime: + +```bash +bin/ncdd test --list +bin/ncdd test phpunit --app libresign -- -c tests/php/phpunit.xml tests/php/Unit/FooTest.php +bin/ncdd test behat --app libresign -- features/file/validate.feature +``` + +The app source must already exist under `volumes/nextcloud/apps-extra/`. NCDD does not clone repositories or receive Git credentials. + +Arguments after `--` are passed directly to the selected test runner as an argument array; NCDD does not evaluate them through a shell. PHPUnit runs as root by default and can be changed with `NCDD_PHPUNIT_USER`. Behat runs as the configured runtime user and supplies the Nextcloud integration environment used by app test suites. + +The initial API intentionally supports only PHPUnit and Behat. Frontend runners, scenario files, and project-specific configuration are not introduced until a concrete consumer requires them. ## Configuration policy -PR #59 intentionally does not introduce `.ncdd.yml`. Environment-specific project contracts may become useful for test-suite definitions later, but adding another configuration format before there is a concrete consumer requirement would duplicate information already present in Compose and `.env`. +NCDD still does not introduce `.ncdd.yml`. Test behavior uses explicit CLI arguments and narrowly scoped environment variables so the repository does not acquire a second configuration model before it is necessary. \ No newline at end of file diff --git a/tests/ncdd.bats b/tests/ncdd.bats index 6c3dec6..59ab9d5 100644 --- a/tests/ncdd.bats +++ b/tests/ncdd.bats @@ -75,7 +75,37 @@ teardown() { [[ "$output" == *"Use bin/ncdd instead of calling docker compose directly."* ]] } -@test "CLI is valid Bash" { - run bash -n "$REPO_ROOT/bin/ncdd" +@test "test --list exposes supported suites" { + run bash "$REPO_ROOT/bin/ncdd" test --list + [ "$status" -eq 0 ] + [ "$output" = phpunit\nbehat' ] +} + +@test "phpunit test runs in the selected app without shell evaluation" { + run bash "$REPO_ROOT/bin/ncdd" test phpunit --app libresign -- -c tests/php/phpunit.xml tests/php/Unit/FooTest.php + [ "$status" -eq 0 ] + grep -q -- '--user root --workdir /var/www/html/apps-extra/libresign app vendor/bin/phpunit -c tests/php/phpunit.xml tests/php/Unit/FooTest.php' "$NCDD_TEST_LOG" +} + +@test "behat test applies the Nextcloud runtime contract" { + run bash "$REPO_ROOT/bin/ncdd" test behat --app libresign -- features/file/validate.feature [ "$status" -eq 0 ] + grep -q -- '--user www-data --workdir /var/www/html/apps-extra/libresign/tests/integration' "$NCDD_TEST_LOG" + grep -q -- '-e BEHAT_ROOT_DIR=/var/www/html' "$NCDD_TEST_LOG" + grep -q -- '-e BEHAT_RUN_AS=www-data' "$NCDD_TEST_LOG" + grep -q -- 'app vendor/bin/behat features/file/validate.feature' "$NCDD_TEST_LOG" } + +@test "test command rejects path traversal in app id" { + run bash "$REPO_ROOT/bin/ncdd" test phpunit --app ../libresign + [ "$status" -ne 0 ] + [[ "$output" == *"invalid app id"* ]] +} + +@test "CLI and test module are valid Bash" { + run bash -n "$REPO_ROOT/bin/ncdd" + [ "$status" -eq 0 ] + + run bash -n "$REPO_ROOT/lib/ncdd-test.sh" + [ "$status" -eq 0 ] +} \ No newline at end of file From 3914b8e131dff4ea112d6ecc718737e03bfd7732 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:52:35 +0000 Subject: [PATCH 3/6] ci(ncdd): validate test command module Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .github/workflows/ncdd-cli.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ncdd-cli.yml b/.github/workflows/ncdd-cli.yml index 77f691e..0452d4b 100644 --- a/.github/workflows/ncdd-cli.yml +++ b/.github/workflows/ncdd-cli.yml @@ -4,6 +4,7 @@ on: pull_request: paths: - 'bin/ncdd' + - 'lib/ncdd-test.sh' - 'tests/ncdd.bats' - 'docs/ncdd.md' - 'AGENTS.md' @@ -14,6 +15,7 @@ on: - main paths: - 'bin/ncdd' + - 'lib/ncdd-test.sh' - 'tests/ncdd.bats' - 'docs/ncdd.md' - 'AGENTS.md' @@ -39,7 +41,7 @@ jobs: file-install: false - name: ShellCheck - run: shellcheck bin/ncdd + run: shellcheck bin/ncdd lib/ncdd-test.sh - name: Test - run: bats tests/ncdd.bats + run: bats tests/ncdd.bats \ No newline at end of file From 02daf0a4a24571145d2b47ef9e37a8b4ce21a6d5 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:53:24 +0000 Subject: [PATCH 4/6] fix(ci): follow ncdd test module in shellcheck Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .github/workflows/ncdd-cli.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ncdd-cli.yml b/.github/workflows/ncdd-cli.yml index 0452d4b..5f835d5 100644 --- a/.github/workflows/ncdd-cli.yml +++ b/.github/workflows/ncdd-cli.yml @@ -41,7 +41,7 @@ jobs: file-install: false - name: ShellCheck - run: shellcheck bin/ncdd lib/ncdd-test.sh + run: shellcheck -x -P SCRIPTDIR bin/ncdd lib/ncdd-test.sh - name: Test run: bats tests/ncdd.bats \ No newline at end of file From e9ee278a037f514f4d69e1995c31afec574b5025 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Wed, 7 Oct 2026 16:06:51 +0000 Subject: [PATCH 5/6] fix(ncdd): correct bats suite list assertion Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/ncdd.bats | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/ncdd.bats b/tests/ncdd.bats index 59ab9d5..ea25cfe 100644 --- a/tests/ncdd.bats +++ b/tests/ncdd.bats @@ -78,7 +78,37 @@ teardown() { @test "test --list exposes supported suites" { run bash "$REPO_ROOT/bin/ncdd" test --list [ "$status" -eq 0 ] - [ "$output" = phpunit\nbehat' ] + [ "$output" = +} + +@test "phpunit test runs in the selected app without shell evaluation" { + run bash "$REPO_ROOT/bin/ncdd" test phpunit --app libresign -- -c tests/php/phpunit.xml tests/php/Unit/FooTest.php + [ "$status" -eq 0 ] + grep -q -- '--user root --workdir /var/www/html/apps-extra/libresign app vendor/bin/phpunit -c tests/php/phpunit.xml tests/php/Unit/FooTest.php' "$NCDD_TEST_LOG" +} + +@test "behat test applies the Nextcloud runtime contract" { + run bash "$REPO_ROOT/bin/ncdd" test behat --app libresign -- features/file/validate.feature + [ "$status" -eq 0 ] + grep -q -- '--user www-data --workdir /var/www/html/apps-extra/libresign/tests/integration' "$NCDD_TEST_LOG" + grep -q -- '-e BEHAT_ROOT_DIR=/var/www/html' "$NCDD_TEST_LOG" + grep -q -- '-e BEHAT_RUN_AS=www-data' "$NCDD_TEST_LOG" + grep -q -- 'app vendor/bin/behat features/file/validate.feature' "$NCDD_TEST_LOG" +} + +@test "test command rejects path traversal in app id" { + run bash "$REPO_ROOT/bin/ncdd" test phpunit --app ../libresign + [ "$status" -ne 0 ] + [[ "$output" == *"invalid app id"* ]] +} + +@test "CLI and test module are valid Bash" { + run bash -n "$REPO_ROOT/bin/ncdd" + [ "$status" -eq 0 ] + + run bash -n "$REPO_ROOT/lib/ncdd-test.sh" + [ "$status" -eq 0 ] +}phpunit\nbehat' ] } @test "phpunit test runs in the selected app without shell evaluation" { From 2296d25befb1482670b743b753bde1668fdd0ee9 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Wed, 7 Oct 2026 16:17:25 +0000 Subject: [PATCH 6/6] fix(ncdd): restore bats test file integrity Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- tests/ncdd.bats | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/tests/ncdd.bats b/tests/ncdd.bats index ea25cfe..5150a53 100644 --- a/tests/ncdd.bats +++ b/tests/ncdd.bats @@ -78,7 +78,7 @@ teardown() { @test "test --list exposes supported suites" { run bash "$REPO_ROOT/bin/ncdd" test --list [ "$status" -eq 0 ] - [ "$output" = + [ "$output" = "$(printf 'phpunit\nbehat')" ] } @test "phpunit test runs in the selected app without shell evaluation" { @@ -108,34 +108,4 @@ teardown() { run bash -n "$REPO_ROOT/lib/ncdd-test.sh" [ "$status" -eq 0 ] -}phpunit\nbehat' ] } - -@test "phpunit test runs in the selected app without shell evaluation" { - run bash "$REPO_ROOT/bin/ncdd" test phpunit --app libresign -- -c tests/php/phpunit.xml tests/php/Unit/FooTest.php - [ "$status" -eq 0 ] - grep -q -- '--user root --workdir /var/www/html/apps-extra/libresign app vendor/bin/phpunit -c tests/php/phpunit.xml tests/php/Unit/FooTest.php' "$NCDD_TEST_LOG" -} - -@test "behat test applies the Nextcloud runtime contract" { - run bash "$REPO_ROOT/bin/ncdd" test behat --app libresign -- features/file/validate.feature - [ "$status" -eq 0 ] - grep -q -- '--user www-data --workdir /var/www/html/apps-extra/libresign/tests/integration' "$NCDD_TEST_LOG" - grep -q -- '-e BEHAT_ROOT_DIR=/var/www/html' "$NCDD_TEST_LOG" - grep -q -- '-e BEHAT_RUN_AS=www-data' "$NCDD_TEST_LOG" - grep -q -- 'app vendor/bin/behat features/file/validate.feature' "$NCDD_TEST_LOG" -} - -@test "test command rejects path traversal in app id" { - run bash "$REPO_ROOT/bin/ncdd" test phpunit --app ../libresign - [ "$status" -ne 0 ] - [[ "$output" == *"invalid app id"* ]] -} - -@test "CLI and test module are valid Bash" { - run bash -n "$REPO_ROOT/bin/ncdd" - [ "$status" -eq 0 ] - - run bash -n "$REPO_ROOT/lib/ncdd-test.sh" - [ "$status" -eq 0 ] -} \ No newline at end of file