qrusty 0.19.2

A trusty priority queue server built with Rust
Documentation
.PHONY: all build doc machete test examples validate-deps smoke-test smoke-test-constrained smoke-test-memory
.PHONY: show-docs
.PHONY: build-qrusty publish-docker
.PHONY: traceability traceability-report
.PHONY: fmt fmt-check lint
.PHONY: fmt-rust fmt-check-rust lint-rust
.PHONY: fmt-py fmt-check-py lint-py
.PHONY: fmt-node fmt-check-node lint-node
.PHONY: fmt-yaml fmt-check-yaml lint-yaml
.PHONY: fmt-md fmt-check-md lint-md
.PHONY: fmt-toml fmt-check-toml lint-toml
.PHONY: coverage coverage-missing coverage-html coverage-summary-json coverage-clean
.PHONY: bench bench-storage bench-http bench-smoke bench-smoke-storage bench-smoke-http
.PHONY: bench-memory bench-http-memory bench-ws-memory
.PHONY: bench-smoke-memory bench-smoke-http-memory bench-smoke-ws-memory
.PHONY: test-live-pyclient test-selenium test-live-nodeclient test-integration-all

PY_RUFF ?= uv tool run ruff
PY_MYPY ?= uv tool run --with types-requests mypy
NODE_PRETTIER ?= npx -y prettier@3.3.3
TAPLO ?= taplo
YAMLLINT ?= uv tool run yamllint
MARKDOWNLINT ?= npx -y markdownlint-cli2

NPM_PUBLISH_ARGS :=
ifdef NPM_OTP
NPM_PUBLISH_ARGS += --otp=$(NPM_OTP)
endif

# Docker platform configuration
DOCKER_PLATFORMS := linux/amd64,linux/arm64

all: build doc machete test examples

build:
	cargo build

doc:
	cargo doc

machete:
	cargo machete

test:
	cargo test

coverage:
	cargo llvm-cov --workspace --all-features

coverage-missing:
	@mkdir -p target/llvm-cov
	cargo llvm-cov --workspace --all-features --text --show-missing-lines --output-path target/llvm-cov/missing.txt
	@echo "Saved: target/llvm-cov/missing.txt"

coverage-html:
	cargo llvm-cov --workspace --all-features --html
	@echo "Open: target/llvm-cov/html/index.html"

coverage-summary-json:
	@mkdir -p target/llvm-cov
	cargo llvm-cov --workspace --all-features --json --summary-only --output-path target/llvm-cov/summary.json
	@echo "Saved: target/llvm-cov/summary.json"

coverage-clean:
	cargo llvm-cov clean

# Benchmark targets
# Full statistical runs — criterion warms up, samples, and writes HTML reports
# to target/criterion/<group>/<case>/report/index.html
bench: bench-storage bench-http bench-memory bench-http-memory bench-ws-memory

bench-storage:
	cargo bench --bench storage_benchmarks

bench-http:
	cargo bench --bench http_benchmarks

bench-memory:
	cargo bench --bench memory_benchmarks

bench-http-memory:
	cargo bench --bench http_memory_benchmarks

bench-ws-memory:
	cargo bench --bench ws_memory_benchmarks

# Smoke runs — one iteration per case, no sampling; fast compile-and-correctness check
bench-smoke: bench-smoke-storage bench-smoke-http bench-smoke-memory bench-smoke-http-memory bench-smoke-ws-memory

bench-smoke-storage:
	cargo bench --bench storage_benchmarks -- --test

bench-smoke-http:
	cargo bench --bench http_benchmarks -- --test

bench-smoke-memory:
	cargo bench --bench memory_benchmarks -- --test

bench-smoke-http-memory:
	cargo bench --bench http_memory_benchmarks -- --test

bench-smoke-ws-memory:
	cargo bench --bench ws_memory_benchmarks -- --test

fmt: fmt-rust fmt-py fmt-node fmt-yaml fmt-md fmt-toml

fmt-check: fmt-check-rust fmt-check-py fmt-check-node fmt-check-yaml fmt-check-md fmt-check-toml

lint: lint-rust lint-py lint-node lint-yaml lint-md lint-toml

fmt-rust:
	cargo fmt --all

fmt-check-rust:
	cargo fmt --all -- --check

lint-rust:
	cargo clippy --all-targets --all-features -- -D warnings

fmt-py:
	@$(PY_RUFF) format scripts integrations/python integrations/examples integrations/pyclient

fmt-check-py:
	@$(PY_RUFF) format --check scripts integrations/python integrations/examples integrations/pyclient

lint-py:
	@$(PY_RUFF) check scripts integrations/python integrations/examples integrations/pyclient
	@$(PY_MYPY) --config-file mypy.ini scripts/traceability.py scripts/smoke_test.py scripts/live_client_tests.py scripts/selenium_ui_tests.py integrations/python integrations/examples integrations/pyclient

fmt-node:
	@$(NODE_PRETTIER) --write --ignore-unknown --ignore-path .gitignore \
		"web_ui/**/*.{ts,tsx,js,jsx,json,css,md,html}" \
		"integrations/nodeclient/**/*.{js,json,md}" \
		"integrations/node-red/**/*.{js,json,md}"

fmt-check-node:
	@$(NODE_PRETTIER) --check --ignore-unknown --ignore-path .gitignore \
		"web_ui/**/*.{ts,tsx,js,jsx,json,css,md,html}" \
		"integrations/nodeclient/**/*.{js,json,md}" \
		"integrations/node-red/**/*.{js,json,md}"

lint-node:
	@# JS syntax check
	@find integrations/nodeclient integrations/node-red -type f -name '*.js' -not -path '*/node_modules/*' -print0 | xargs -0 -r -n 1 node --check
	@# TS typecheck (does not emit) - only if deps are installed
	@if [ -d webui/node_modules ]; then \
		npx -y --package=typescript@5.4.5 tsc -p webui/tsconfig.json --noEmit; \
	else \
		echo "Skipping webui typecheck (run 'make build-webui' or 'cd webui && npm install')"; \
	fi

# YAML targets (prettier for format, yamllint for lint)
YAML_GLOB := $(shell find . -name "*.yml" -o -name "*.yaml" 2>/dev/null \
	| grep -v "^\./\.venv" | grep -v "^\./target" \
	| grep -v "^\./\.git" | grep -v "/node_modules/")

fmt-yaml:
	@$(NODE_PRETTIER) --write --ignore-unknown --ignore-path .gitignore \
		"**/*.yml" "**/*.yaml"

fmt-check-yaml:
	@$(NODE_PRETTIER) --check --ignore-unknown --ignore-path .gitignore \
		"**/*.yml" "**/*.yaml"

lint-yaml:
	@$(YAMLLINT) --config-file .yamllint.yml $(YAML_GLOB)

# Markdown targets (prettier for format, markdownlint-cli2 for lint)
fmt-md:
	@$(NODE_PRETTIER) --write --ignore-unknown --ignore-path .gitignore \
		"**/*.md"

fmt-check-md:
	@$(NODE_PRETTIER) --check --ignore-unknown --ignore-path .gitignore \
		"**/*.md"

lint-md:
	@$(MARKDOWNLINT) "**/*.md" "!.venv/**" "!target/**" "!**/node_modules/**" "!.git/**"

# TOML targets (taplo for format, format-check, and lint)
fmt-toml:
	@$(TAPLO) fmt Cargo.toml crates/*/Cargo.toml integrations/pyclient/pyproject.toml

fmt-check-toml:
	@$(TAPLO) fmt --check Cargo.toml crates/*/Cargo.toml integrations/pyclient/pyproject.toml

lint-toml:
	@$(TAPLO) lint Cargo.toml crates/*/Cargo.toml integrations/pyclient/pyproject.toml

examples:
	@echo "Testing Python integration example..."
	@cd integrations/python && python3 -m py_compile qrusty_publish.py
	@cd integrations/python && python3 -m py_compile qrusty_consume.py
	@cd integrations/python && python3 -m py_compile qrusty_demo.py
	@cd integrations/python && test -f requirements.txt && echo "Python requirements file exists" || echo "Python requirements missing"
	@echo "Testing Python queue management examples..."
	@cd integrations/examples && python3 -m py_compile queue_management_examples.py
	@echo "Validating Node-RED integration..."
	@cd integrations/node-red && (command -v node >/dev/null 2>&1 && node -e "JSON.parse(require('fs').readFileSync('package.json', 'utf8'))" || echo "Node.js not available, skipping Node-RED validation")
	@cd integrations/node-red && test -f qrusty-publish.js && echo "Node-RED publish example exists" || echo "Node-RED publish example missing"
	@cd integrations/node-red && test -f qrusty-consume.js && echo "Node-RED consume example exists" || echo "Node-RED consume example missing"
	@echo "Validating environment example..."
	@test -f example.env && echo "Environment example exists" || echo "Environment example missing"
	@echo "Validating documentation examples..."
	@test -f integrations/examples/priority_examples.md && echo "Priority examples documentation exists" || echo "Priority examples documentation missing"
	@test -f integrations/examples/api_usage_examples.md && echo "API usage examples documentation exists" || echo "API usage examples documentation missing"
	@test -f integrations/examples/queue_management_guide.md && echo "Queue management guide exists" || echo "Queue management guide missing"
	@echo "All available integration examples validated successfully!"

show-docs:
	cargo doc --no-deps --open

# Traceability targets - run in venv with doorstop installed
# Uses uv if available, otherwise falls back to standard venv activation
traceability:
	@echo "Generating traceability report..."
	@if command -v uv >/dev/null 2>&1; then \
		uv run --python .venv/bin/python scripts/traceability.py; \
	else \
		. .venv/bin/activate && python scripts/traceability.py; \
	fi

traceability-report:
	@echo "Generating traceability report to docs/traceability_report.md..."
	@if command -v uv >/dev/null 2>&1; then \
		uv run --python .venv/bin/python scripts/traceability.py > docs/traceability_report.md; \
	else \
		. .venv/bin/activate && python scripts/traceability.py > docs/traceability_report.md; \
	fi
	@echo "Report saved to docs/traceability_report.md"

build-qrusty:
	docker buildx build --no-cache --pull --platform $(DOCKER_PLATFORMS) -t greeng340or/qrusty -f Dockerfile --push .

publish-docker:
	$(eval VERSION := $(shell grep '^version' Cargo.toml | head -n1 | sed 's/.*"\(.*\)".*/\1/'))
	docker buildx build --no-cache --pull --platform $(DOCKER_PLATFORMS) \
		-t greeng340or/qrusty:$(VERSION) \
		-t greeng340or/qrusty:latest \
		-f Dockerfile --push .

publish-client:
	cd crates/qrusty_client && cargo publish

# Python Client
build-pyclient:
	rm -rf integrations/pyclient/dist
	cd integrations/pyclient && python3 -m build

test-pyclient:
	cd integrations/pyclient && python3 -m unittest discover -s tests

doc-pyclient:
	cd integrations/pyclient && pdoc qrusty_pyclient.py -o docs

publish-pyclient:
	cd integrations/pyclient && python3 -m twine upload dist/*

# Node Client
build-nodeclient:
	cd integrations/nodeclient && npm install

test-nodeclient:
	cd integrations/nodeclient && npm test

doc-nodeclient:
	cd integrations/nodeclient && npm run docs

publish-nodeclient:
	@cd integrations/nodeclient && (set -e; \
		trap 'rm -f .npmrc' EXIT; \
		printf "%s\n" "//registry.npmjs.org/:_authToken=$$NPM_TOKEN" > .npmrc; \
		npm publish $(NPM_PUBLISH_ARGS))

publish-node-red:
	@cd integrations/node-red && (set -e; \
		trap 'rm -f .npmrc' EXIT; \
		printf "%s\n" "//registry.npmjs.org/:_authToken=$$NPM_TOKEN" > .npmrc; \
		npm publish $(NPM_PUBLISH_ARGS))

# Node-RED integration
build-node-red:
	@echo "Node-RED integration has no build step (JS snippets only)"

test-node-red:
	@# Validate package.json and JS syntax
	@cd integrations/node-red && node -e "JSON.parse(require('fs').readFileSync('package.json', 'utf8'))"
	@find integrations/node-red -type f -name '*.js' -not -path '*/node_modules/*' -print0 | xargs -0 -r -n 1 node --check

pack-node-red:
	@cd integrations/node-red && npm pack --dry-run

# Web UI
build-webui:
	cd web_ui && npm install && npm run build

clean-webui:
	rm -rf static_webui

run-dev:
	make build
	make build-webui
	echo "Starting qrusty server..."
	echo "Access the UI at: http://localhost:6784/ui"
	DATA_PATH=/tmp/qrusty/data WEBUI_DIR=static_webui cargo run

# Validate dependencies (requires updated Docker containers)
validate-deps:
	@if test -f scripts/validate_dependencies.sh; then \
		chmod +x scripts/validate_dependencies.sh && ./scripts/validate_dependencies.sh; \
	else \
		echo "Dependency validation script not found"; \
	fi

# Live smoke test — starts a real qrusty binary and exercises it end-to-end.
# Implements: SYS-0011
# Pass extra args via SMOKE_ARGS, e.g.:  make smoke-test SMOKE_ARGS="--duration 60"
#
# Resolves the binary path by asking rustc for the host target triple so the
# correct platform-specific subdirectory is used even when ~/.cargo/config.toml
# sets a non-default build target.
CARGO_HOST_TARGET := $(shell rustc -vV 2>/dev/null | awk '/^host:/{print $$2}')
QRUSTY_DEBUG_BIN  := target/$(CARGO_HOST_TARGET)/debug/qrusty

smoke-test: build
	@echo "Running live smoke test (Implements: SYS-0011) ..."
	@if command -v uv >/dev/null 2>&1; then \
		QRUSTY_MEMORY_PRESSURE_THRESHOLD=0.99 uv run --python .venv/bin/python scripts/smoke_test.py --binary $(QRUSTY_DEBUG_BIN) $(SMOKE_ARGS); \
	else \
		QRUSTY_MEMORY_PRESSURE_THRESHOLD=0.99 . .venv/bin/activate && python scripts/smoke_test.py --binary $(QRUSTY_DEBUG_BIN) $(SMOKE_ARGS); \
	fi

smoke-test-constrained: build
	@echo "Running smoke test with constrained memory (256 MB) ..."
	@echo "This validates memory pressure detection and load shedding."
	@QRUSTY_MEMORY_LIMIT_MB=256 \
	 QRUSTY_HOT_TIER_SIZE=100 \
	 QRUSTY_REFILL_THRESHOLD=25 \
	 ROCKSDB_CACHE_MB=16 \
	 ROCKSDB_WRITE_BUFFER_MB=8 \
	 $(if $(shell command -v uv 2>/dev/null), \
		uv run --python .venv/bin/python scripts/smoke_test.py --binary $(QRUSTY_DEBUG_BIN) $(SMOKE_ARGS), \
		. .venv/bin/activate && python scripts/smoke_test.py --binary $(QRUSTY_DEBUG_BIN) $(SMOKE_ARGS))

smoke-test-memory: build
	@echo "Running live smoke test with in-memory storage (Implements: SYS-0013) ..."
	@if command -v uv >/dev/null 2>&1; then \
		STORAGE_MODE=memory QRUSTY_MEMORY_PRESSURE_THRESHOLD=0.99 uv run --python .venv/bin/python scripts/smoke_test.py --binary $(QRUSTY_DEBUG_BIN) $(SMOKE_ARGS); \
	else \
		STORAGE_MODE=memory QRUSTY_MEMORY_PRESSURE_THRESHOLD=0.99 . .venv/bin/activate && python scripts/smoke_test.py --binary $(QRUSTY_DEBUG_BIN) $(SMOKE_ARGS); \
	fi

# Live integration tests — start a real server and exercise client libraries
LIVE_TEST_PORT    ?= 17785
SELENIUM_PORT     ?= 17786
NODE_LIVE_PORT    ?= 17787
SELENIUM_HUB      ?= http://172.17.0.1:4444
QRUSTY_HOST       ?= $(shell hostname -I | awk '{print $$1}')

test-live-pyclient: build
	@echo "Running Python live client tests ..."
	@if command -v uv >/dev/null 2>&1; then \
		uv run --python .venv/bin/python scripts/live_client_tests.py \
			--binary $(QRUSTY_DEBUG_BIN) --port $(LIVE_TEST_PORT); \
	else \
		. .venv/bin/activate && python scripts/live_client_tests.py \
			--binary $(QRUSTY_DEBUG_BIN) --port $(LIVE_TEST_PORT); \
	fi

test-selenium: build
	@echo "Running Selenium UI tests (hub: $(SELENIUM_HUB)) ..."
	@if command -v uv >/dev/null 2>&1; then \
		uv run --python .venv/bin/python scripts/selenium_ui_tests.py \
			--binary $(QRUSTY_DEBUG_BIN) --port $(SELENIUM_PORT) \
			--selenium-hub $(SELENIUM_HUB) --qrusty-host $(QRUSTY_HOST); \
	else \
		. .venv/bin/activate && python scripts/selenium_ui_tests.py \
			--binary $(QRUSTY_DEBUG_BIN) --port $(SELENIUM_PORT) \
			--selenium-hub $(SELENIUM_HUB) --qrusty-host $(QRUSTY_HOST); \
	fi

test-live-nodeclient: build
	@echo "Running Node.js live client tests ..."
	@node integrations/nodeclient/live_test.js \
		--binary $(QRUSTY_DEBUG_BIN) --port $(NODE_LIVE_PORT)

test-integration-all: test test-live-pyclient test-live-nodeclient test-selenium smoke-test
	@echo "All integration tests complete."