From e1d3891bd19788c00d818eec0a53389fe1f131c9 Mon Sep 17 00:00:00 2001 From: "jason.woltje" Date: Thu, 16 Jul 2026 17:31:30 -0500 Subject: [PATCH] fix(glpi): accept partial content in list wrappers Accept HTTP 206 responses from ranged GLPI list endpoints while preserving failures for genuine error statuses. Add red-first regression coverage across ticket, computer, and user lists. Closes #807 Co-Authored-By: OpenAI GPT --- docs/scratchpads/807-glpi-partial-content.md | 64 ++++++++++++++ .../framework/tools/glpi/computer-list.sh | 2 +- .../tools/glpi/test-list-http-status.sh | 84 +++++++++++++++++++ .../framework/tools/glpi/ticket-list.sh | 2 +- .../mosaic/framework/tools/glpi/user-list.sh | 2 +- 5 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 docs/scratchpads/807-glpi-partial-content.md create mode 100755 packages/mosaic/framework/tools/glpi/test-list-http-status.sh diff --git a/docs/scratchpads/807-glpi-partial-content.md b/docs/scratchpads/807-glpi-partial-content.md new file mode 100644 index 0000000..492eaca --- /dev/null +++ b/docs/scratchpads/807-glpi-partial-content.md @@ -0,0 +1,64 @@ +# Issue #807 — GLPI list wrappers accept HTTP 206 + +- **Branch:** `fix/807-glpi-206` +- **Task:** Gitea issue #807 +- **Role:** Author-only worker reporting to `mosaic-100`; no self-review or merge +- **Started:** 2026-07-16 + +## Objective + +Fix the shipped GLPI ticket, computer, and user list wrappers so ranged responses with HTTP 206 Partial Content render successfully while genuine HTTP failures remain non-zero errors. + +## Scope + +- Modify only the three affected list wrappers and a focused shell regression test. +- Do not touch `session-init.sh`, `ticket-create.sh`, or `docs/TASKS.md`. +- Add task-local delivery evidence here as required by the mission protocol. + +## Plan + +1. Add a deterministic shell harness that copies each wrapper beside stubbed `session-init.sh`, credentials, and `curl` boundaries. +2. Prove RED against the current 200-only gates: 206 must fail before the implementation change. +3. Update all three status gates to accept exactly 200 or 206. +4. Prove GREEN for 206 rendering and genuine 401/500 failures, then run repository quality gates. +5. Commit with co-author attribution, run the push queue guard, push, and open a PR for independent review and merge by the team lead. + +## Budget + +- No explicit token cap supplied. +- Soft estimate: 8K tokens; narrow single-worker execution with no exploratory scope. + +## Progress + +- [x] Mission, task, PRD, QA, documentation, and code-review guidance loaded. +- [x] RED regression evidence captured: `test-list-http-status.sh` exited 1; all three wrappers rejected 206 while retaining 401 failures. +- [x] Implementation complete. +- [x] Relevant tests and repository gates green. +- [ ] Commit pushed and PR opened. + +## Tests and evidence + +- RED (before source fix): `packages/mosaic/framework/tools/glpi/test-list-http-status.sh` → exit 1; ticket/computer/user 206 assertions failed, all 401 assertions passed. +- GREEN: `bash -n packages/mosaic/framework/tools/glpi/{ticket-list.sh,computer-list.sh,user-list.sh,test-list-http-status.sh}` → pass. +- GREEN: `shellcheck packages/mosaic/framework/tools/glpi/test-list-http-status.sh` → pass. +- GREEN: `packages/mosaic/framework/tools/glpi/test-list-http-status.sh` → 6 assertions pass (206 renders and 401 errors for all three wrappers). +- GREEN: `pnpm typecheck` → 42/42 tasks pass. +- GREEN: `pnpm lint` → 23/23 tasks pass. +- GREEN: `pnpm format:check` → all matched files pass. +- Setup note: initial gate attempts could not start because the fresh worktree lacked dependencies; `pnpm install --frozen-lockfile --store-dir /home/hermes/.local/share/pnpm/store` restored the locked workspace dependencies without lockfile changes. + +## Acceptance criteria mapping + +| Criterion | Evidence | +| --- | --- | +| HTTP 206 succeeds and renders each ranged list | Focused test's three 206 render assertions pass | +| Genuine HTTP failures remain non-zero with existing diagnostics | Focused test's three HTTP 401 assertions pass | +| Only affected list wrappers change | Diff contains the three status predicates plus focused test/evidence; session and create wrappers untouched | + +## Documentation decision + +No operator/API documentation change is needed: this restores documented list behavior for a healthy GLPI response without changing command syntax, output, configuration, or public contracts. This task scratchpad records delivery evidence. + +## Risks / blockers + +- Existing dirty `.mosaic/orchestrator/mission.json` and `.mosaic/orchestrator/session.lock` are runtime-owned and will not be edited or committed. diff --git a/packages/mosaic/framework/tools/glpi/computer-list.sh b/packages/mosaic/framework/tools/glpi/computer-list.sh index 9532531..c580f5a 100755 --- a/packages/mosaic/framework/tools/glpi/computer-list.sh +++ b/packages/mosaic/framework/tools/glpi/computer-list.sh @@ -37,7 +37,7 @@ response=$(curl -sk -w "\n%{http_code}" \ http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') -if [[ "$http_code" != "200" ]]; then +if [[ "$http_code" != "200" && "$http_code" != "206" ]]; then echo "Error: Failed to list computers (HTTP $http_code)" >&2 exit 1 fi diff --git a/packages/mosaic/framework/tools/glpi/test-list-http-status.sh b/packages/mosaic/framework/tools/glpi/test-list-http-status.sh new file mode 100755 index 0000000..8f14b38 --- /dev/null +++ b/packages/mosaic/framework/tools/glpi/test-list-http-status.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# Regression harness for #807: ranged GLPI list requests may return HTTP 206. +# +# Each shipped list wrapper must render a healthy 206 response and must retain +# its non-zero error behavior for a genuine HTTP failure. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/glpi-list-http-status}" +TOOL_DIR="$WORK_DIR/tools/glpi" +BIN_DIR="$WORK_DIR/bin" + +rm -rf "$WORK_DIR" +mkdir -p "$TOOL_DIR" "$BIN_DIR" "$WORK_DIR/tools/_lib" +trap 'rm -rf "$WORK_DIR"' EXIT + +for wrapper in ticket-list.sh computer-list.sh user-list.sh; do + cp "$SCRIPT_DIR/$wrapper" "$TOOL_DIR/$wrapper" +done + +cat > "$WORK_DIR/tools/_lib/credentials.sh" <<'SH' +load_credentials() { + export GLPI_URL="https://glpi.test/apirest.php" + export GLPI_APP_TOKEN="test-app-token" +} +SH + +cat > "$TOOL_DIR/session-init.sh" <<'SH' +#!/usr/bin/env bash +printf '%s\n' 'test-session-token' +SH +chmod +x "$TOOL_DIR/session-init.sh" + +cat > "$BIN_DIR/curl" <<'SH' +#!/usr/bin/env bash +set -euo pipefail +printf '%s\n' '[{"id":42,"priority":3,"status":1,"name":"Regression fixture","date_mod":"2026-07-16 10:00:00","serial":"SER-42","states_id":1,"realname":"Fixture","firstname":"GLPI","is_active":1}]' +printf '%s\n' "${GLPI_TEST_HTTP_CODE:?GLPI_TEST_HTTP_CODE is required}" +SH +chmod +x "$BIN_DIR/curl" + +export MOSAIC_HOME="$WORK_DIR" +export PATH="$BIN_DIR:$PATH" + +wrappers=(ticket-list.sh computer-list.sh user-list.sh) +resources=(tickets computers users) +headings=(PRIORITY SERIAL USERNAME) +fail=0 + +for index in "${!wrappers[@]}"; do + wrapper="${wrappers[$index]}" + resource="${resources[$index]}" + heading="${headings[$index]}" + path="$TOOL_DIR/$wrapper" + + if ! output=$(GLPI_TEST_HTTP_CODE=206 bash "$path" 2>&1); then + echo "FAIL: $wrapper rejected healthy HTTP 206" >&2 + fail=1 + elif [[ "$output" != *"$heading"* || "$output" != *"Regression fixture"* ]]; then + echo "FAIL: $wrapper did not render the HTTP 206 list response" >&2 + fail=1 + else + echo "PASS: $wrapper renders HTTP 206" + fi + + rc=0 + output=$(GLPI_TEST_HTTP_CODE=401 bash "$path" 2>&1) || rc=$? + if [[ "$rc" -eq 0 ]]; then + echo "FAIL: $wrapper accepted HTTP 401" >&2 + fail=1 + elif [[ "$output" != *"Error: Failed to list $resource (HTTP 401)"* ]]; then + echo "FAIL: $wrapper changed the HTTP failure diagnostic" >&2 + fail=1 + else + echo "PASS: $wrapper rejects HTTP 401" + fi +done + +if [[ "$fail" -eq 0 ]]; then + echo "ALL PASS: test-list-http-status.sh" +fi + +exit "$fail" diff --git a/packages/mosaic/framework/tools/glpi/ticket-list.sh b/packages/mosaic/framework/tools/glpi/ticket-list.sh index 7538f5d..4e419b3 100755 --- a/packages/mosaic/framework/tools/glpi/ticket-list.sh +++ b/packages/mosaic/framework/tools/glpi/ticket-list.sh @@ -55,7 +55,7 @@ response=$(curl -sk -w "\n%{http_code}" \ http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') -if [[ "$http_code" != "200" ]]; then +if [[ "$http_code" != "200" && "$http_code" != "206" ]]; then echo "Error: Failed to list tickets (HTTP $http_code)" >&2 exit 1 fi diff --git a/packages/mosaic/framework/tools/glpi/user-list.sh b/packages/mosaic/framework/tools/glpi/user-list.sh index b62b7cb..39bc5ba 100755 --- a/packages/mosaic/framework/tools/glpi/user-list.sh +++ b/packages/mosaic/framework/tools/glpi/user-list.sh @@ -37,7 +37,7 @@ response=$(curl -sk -w "\n%{http_code}" \ http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') -if [[ "$http_code" != "200" ]]; then +if [[ "$http_code" != "200" && "$http_code" != "206" ]]; then echo "Error: Failed to list users (HTTP $http_code)" >&2 exit 1 fi