Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ncdd-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
paths:
- 'bin/ncdd'
- 'lib/ncdd-test.sh'
- 'tests/ncdd.bats'
- 'docs/ncdd.md'
- 'AGENTS.md'
Expand All @@ -14,6 +15,7 @@ on:
- main
paths:
- 'bin/ncdd'
- 'lib/ncdd-test.sh'
- 'tests/ncdd.bats'
- 'docs/ncdd.md'
- 'AGENTS.md'
Expand All @@ -39,7 +41,7 @@ jobs:
file-install: false

- name: ShellCheck
run: shellcheck bin/ncdd
run: shellcheck -x -P SCRIPTDIR bin/ncdd lib/ncdd-test.sh

- name: Test
run: bats tests/ncdd.bats
run: bats tests/ncdd.bats
11 changes: 11 additions & 0 deletions bin/ncdd
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -181,6 +189,9 @@ case "$command" in
doctor)
doctor "$@"
;;
test)
ncdd_test "$@"
;;
*)
fail "unknown command: $command"
;;
Expand Down
20 changes: 18 additions & 2 deletions docs/ncdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<app-id>`. 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.
110 changes: 110 additions & 0 deletions lib/ncdd-test.sh
Original file line number Diff line number Diff line change
@@ -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
}
32 changes: 31 additions & 1 deletion tests/ncdd.bats
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,37 @@ teardown() {
[[ "$output" == *"Use bin/ncdd instead of calling docker compose directly."* ]]
}

@test "CLI is valid Bash" {
@test "test --list exposes supported suites" {
run bash "$REPO_ROOT/bin/ncdd" test --list
[ "$status" -eq 0 ]
[ "$output" = "$(printf '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 ]
}
Loading