informix-db/Makefile
Ryan Malloy ea00990774 Phase 1 polish: PDU match test catches a real capability-int bug
Polish item #1: byte-for-byte regression test that asserts our
generated login PDU is structurally identical to JDBC's reference
captured in docs/CAPTURES/01-connect-only.socat.log.

The test (tests/test_pdu_match.py) immediately caught a real bug:
the capability section was misread during Phase 0 byte-decoding.
Earlier text claimed Cap_1=1, Cap_2=0x3c000000, Cap_3=0 — actually:

  Cap_1 = 0x0000013c   (= (capability_class << 8) | protocol_version
                          where protocol_version = 0x3c = PF_PROT_SQLI_0600)
  Cap_2 = 0
  Cap_3 = 0

The misalignment was: the 0x3c byte I attributed to Cap_2's high
byte was actually Cap_1's low byte. The dev-image server is
permissive enough to accept arbitrary capability values, so the
connection succeeded even with the wrong bytes — but the PDU wasn't
structurally identical to JDBC's reference. SERVER-ACCEPTS ≠
STRUCTURALLY-CORRECT. This is exactly why the byte-for-byte diff
was the right polish item; "it connects" was a false ceiling.

After fix:
- 6 PDU-match tests assert byte-for-byte equality at offsets 2..280
  (the structural prefix: SLheader sans length, all login markers,
  capability ints, username, password, protocol IDs, env vars).
- Bytes 280+ legitimately differ per process (PID, TID, hostname,
  cwd, AppName) — those are NOT asserted.
- Length field (offsets 0..1) also legitimately differs because our
  PDU has shorter env list and AppName.
- Test uses monkey-patched IfxSocket so no network is needed.

Polish item #2: Makefile per global CLAUDE.md convention. Targets:
install, lint, format, test, test-integration, test-all, test-pdu,
ifx-up/down/logs/shell/status, capture (re-run JDBC scenarios under
socat), clean. `make` (no target) prints help.

Doc updates:
- PROTOCOL_NOTES.md §12: corrected capability section with the
  actual values and an explanation of the methodology lesson
- DECISION_LOG.md: new entry recording the correction with a
  pointer to the regression test and the takeaway

Side artifacts:
- docs/CAPTURES/03-py-connect-only.socat.log
- docs/CAPTURES/04-py-no-database.socat.log
- docs/CAPTURES/05-py-fixed-caps.socat.log

Test counts: 40 unit + 6 integration = 46 total, all green, ruff clean.
2026-05-02 20:18:03 -06:00

92 lines
3.3 KiB
Makefile

# informix-db — common dev commands
#
# uv-managed; run `make help` for the full target list.
# Image digest pinned in tests/docker-compose.yml; mirrored here for
# tab-complete-able commands like `make ifx-logs` that don't go through
# docker-compose.
IFX_CONTAINER ?= informix-db-test
.PHONY: help install lint format test test-integration test-all test-pdu \
ifx-up ifx-down ifx-logs ifx-shell ifx-status \
capture clean
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) \
| awk -F':.*## ' '{printf " %-20s %s\n", $$1, $$2}'
# ----------------------------------------------------------------------------
# Python / dev workflow
# ----------------------------------------------------------------------------
install: ## Sync dev dependencies (uv sync --extra dev)
uv sync --extra dev
lint: ## Run ruff
uv run ruff check src/ tests/
format: ## Auto-format with ruff
uv run ruff format src/ tests/
uv run ruff check src/ tests/ --fix
test: ## Run unit tests (no Docker required)
uv run pytest
test-integration: ## Run integration tests (needs Informix container; see `make ifx-up`)
uv run pytest -m integration
test-all: ## Run unit + integration tests
uv run pytest -m ""
test-pdu: ## Run only the JDBC-vs-Python PDU regression test
uv run pytest tests/test_pdu_match.py -v
# ----------------------------------------------------------------------------
# Informix dev container
# ----------------------------------------------------------------------------
ifx-up: ## Start the Informix dev container (pinned by digest)
docker compose -f tests/docker-compose.yml up -d
@echo " Container: $(IFX_CONTAINER)"
@echo " Listener: 127.0.0.1:9088 (SQLI native)"
@echo " Login: informix / in4mix on database sysmaster"
ifx-down: ## Stop and remove the Informix container
docker compose -f tests/docker-compose.yml down
ifx-logs: ## Tail the container logs
docker logs -f $(IFX_CONTAINER)
ifx-shell: ## Drop into a shell inside the container
docker exec -it $(IFX_CONTAINER) bash
ifx-status: ## Check container health and listener readiness
@docker ps --filter name=$(IFX_CONTAINER) --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
@nc -zv 127.0.0.1 9088 2>&1 | head -1
# ----------------------------------------------------------------------------
# Phase 0 spike: re-capture wire traffic against the dev container
# ----------------------------------------------------------------------------
capture: ## Re-capture all three reference scenarios (JDBC) under socat
@for s in connect-only select-1 dml-cycle; do \
echo "=== $$s ==="; \
socat -d -d -x TCP-LISTEN:9090,reuseaddr TCP:127.0.0.1:9088 \
2>"docs/CAPTURES/$$s.socat.log" & \
SOCAT_PID=$$!; \
sleep 0.4; \
IFX_PORT=9090 java -cp "build/ifxjdbc.jar:build/" tests.reference.RefClient $$s; \
sleep 0.3; \
kill $$SOCAT_PID 2>/dev/null; \
wait 2>/dev/null; \
echo " → docs/CAPTURES/$$s.socat.log"; \
done
# ----------------------------------------------------------------------------
# Cleanup
# ----------------------------------------------------------------------------
clean: ## Remove build artifacts and caches (keeps captures and decompiled JDBC source)
rm -rf dist/ .pytest_cache/ .ruff_cache/ .mypy_cache/
find src tests -name __pycache__ -type d -exec rm -rf {} +