feat: add inventory metadata updates

This commit is contained in:
lussierj committed 2026-07-28 19:30:51 +00:00
1 parent 51aec1b404
commit a87ab2709f
11 files changed
+1048 -12

No files matched your search

+219
View File
@@ -0,0 +1,219 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${repo_root}"
project_name="${MINEROPS_ASSET_HISTORY_TEST_PROJECT:-minerops-asset-history-$$}"
empty_database="minerops_asset_history_empty"
compose=(
docker compose
--project-name "${project_name}"
--env-file .env.dev
-f compose.dev.yaml
)
cleanup() {
status=$?
if (( status != 0 )); then
echo
echo "Asset ID history migration test failed. Recent PostgreSQL logs:"
"${compose[@]}" logs --no-color --tail 120 postgres || true
fi
"${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true
exit "${status}"
}
trap cleanup EXIT
if [[ ! -f .env.dev ]]; then
echo "Missing .env.dev. Run ./scripts/dev-env-init.sh first." >&2
exit 1
fi
run_alembic() {
"${compose[@]}" run --rm --no-deps backend alembic "$@"
}
echo "Starting disposable PostgreSQL..."
"${compose[@]}" up --detach postgres
for attempt in $(seq 1 60); do
if "${compose[@]}" exec -T postgres sh -ec \
'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
>/dev/null 2>&1; then
break
fi
if (( attempt == 60 )); then
echo "PostgreSQL did not become ready." >&2
exit 1
fi
sleep 1
done
echo "Building migration image..."
"${compose[@]}" build backend
echo "Upgrading the current head to Asset ID history..."
run_alembic upgrade 0008_manual_miner_inventory
run_alembic upgrade head
current_revision="$(run_alembic current 2>/dev/null)"
[[ "${current_revision}" == *"0009_inventory_asset_id_history"* ]]
echo "Checking history schema, constraints, foreign keys, and indexes..."
"${compose[@]}" exec -T postgres sh -ec \
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
<<'SQL'
DO $$
DECLARE
v_role_id UUID;
BEGIN
IF to_regclass('public.miner_asset_id_history') IS NULL THEN
RAISE EXCEPTION 'Asset ID history table was not created';
END IF;
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'miner_asset_id_history'
AND column_name = 'old_asset_id'
AND is_nullable = 'YES'
) OR EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'miner_asset_id_history'
AND column_name IN ('miner_id', 'new_asset_id', 'changed_at', 'changed_by')
AND is_nullable <> 'NO'
) THEN
RAISE EXCEPTION 'Asset ID history nullability is incorrect';
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_indexes
WHERE schemaname = 'public'
AND tablename = 'miner_asset_id_history'
AND indexname = 'idx_miner_asset_id_history_miner_changed_at'
AND indexdef LIKE '%(miner_id, changed_at, id)%'
) THEN
RAISE EXCEPTION 'Chronological miner history index is missing';
END IF;
SELECT id INTO v_role_id FROM roles WHERE name = 'Admin';
INSERT INTO users (
id, username, display_name, password_hash, role_id
) VALUES (
'00000000-0000-0000-0000-000000000901',
'asset-history-test',
'Asset History Test',
'not-used',
v_role_id
);
INSERT INTO miners (id, asset_tag, status)
VALUES (
'00000000-0000-0000-0000-000000000902',
'ASSET-OLD',
'manual'
);
INSERT INTO miner_asset_id_history (
miner_id, old_asset_id, new_asset_id, changed_at, changed_by
) VALUES
(
'00000000-0000-0000-0000-000000000902',
'ASSET-OLD',
'ASSET-NEW',
'2026-01-01 00:00:00+00',
'00000000-0000-0000-0000-000000000901'
),
(
'00000000-0000-0000-0000-000000000902',
'ASSET-NEW',
'ASSET-LATEST',
'2026-01-02 00:00:00+00',
'00000000-0000-0000-0000-000000000901'
);
BEGIN
INSERT INTO miner_asset_id_history (
miner_id, old_asset_id, new_asset_id, changed_by
) VALUES (
'00000000-0000-0000-0000-000000000902',
' ASSET-OLD ',
'ASSET-BAD',
'00000000-0000-0000-0000-000000000901'
);
RAISE EXCEPTION 'Unnormalized old Asset ID was accepted';
EXCEPTION WHEN check_violation THEN
NULL;
END;
BEGIN
INSERT INTO miner_asset_id_history (
miner_id, old_asset_id, new_asset_id, changed_by
) VALUES (
'00000000-0000-0000-0000-000000000999',
NULL,
'ASSET-NEW',
'00000000-0000-0000-0000-000000000901'
);
RAISE EXCEPTION 'Unknown miner history was accepted';
EXCEPTION WHEN foreign_key_violation THEN
NULL;
END;
BEGIN
INSERT INTO miner_asset_id_history (
miner_id, old_asset_id, new_asset_id, changed_by
) VALUES (
'00000000-0000-0000-0000-000000000902',
'ASSET-OLD',
'ASSET-NEW',
NULL
);
RAISE EXCEPTION 'History without an actor was accepted';
EXCEPTION WHEN not_null_violation THEN
NULL;
END;
BEGIN
DELETE FROM miners
WHERE id = '00000000-0000-0000-0000-000000000902';
RAISE EXCEPTION 'Miner with permanent Asset ID history was deleted';
EXCEPTION WHEN foreign_key_violation THEN
NULL;
END;
END
$$;
SQL
echo "Downgrading and re-upgrading Asset ID history..."
run_alembic downgrade 0008_manual_miner_inventory
table_after_downgrade="$(
"${compose[@]}" exec -T postgres sh -ec \
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -At' <<'SQL'
SELECT to_regclass('public.miner_asset_id_history');
SQL
)"
if [[ -n "${table_after_downgrade}" ]]; then
echo "Asset ID history table remained after downgrade." >&2
exit 1
fi
run_alembic upgrade head
echo "Migrating an empty database from base through head..."
"${compose[@]}" exec -T postgres sh -ec \
'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d postgres \
-c "CREATE DATABASE minerops_asset_history_empty"' >/dev/null
"${compose[@]}" run --rm --no-deps \
-e "POSTGRES_DB=${empty_database}" \
backend alembic upgrade head
empty_revision="$(
"${compose[@]}" run --rm --no-deps \
-e "POSTGRES_DB=${empty_database}" \
backend alembic current 2>/dev/null
)"
[[ "${empty_revision}" == *"0009_inventory_asset_id_history"* ]]
echo "Asset ID history migration test passed."
+1 -1
View File
@@ -88,7 +88,7 @@ health_json="$(cat "${health_file}")"
echo "Checking migrations and required schema..."
current_revision="$("${compose[@]}" exec -T backend alembic current 2>/dev/null)"
[[ "${current_revision}" == *"0008_manual_miner_inventory"* ]]
[[ "${current_revision}" == *"0009_inventory_asset_id_history"* ]]
missing_tables="$(
"${compose[@]}" exec -T postgres sh -c \