ci(F6.1): create a real inspection after territory preload
This commit is contained in:
@@ -0,0 +1,247 @@
|
|||||||
|
name: Inspection planning smoke
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'api-v3/src/inspection-visits/**'
|
||||||
|
- 'api-v3/src/database/migrations/**'
|
||||||
|
- 'api-v3/src/reference-data/**'
|
||||||
|
- 'web-v2/src/pages/InspectionVisitCreateF61Page.tsx'
|
||||||
|
- 'web-v2/src/pages/FieldBriefingsPage.tsx'
|
||||||
|
- 'web-v2/src/layout/AppLayout.tsx'
|
||||||
|
- '.github/workflows/inspection-planning-smoke.yml'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-inspection:
|
||||||
|
name: F6.1 · real inspection create
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Migrate presentation database
|
||||||
|
run: |
|
||||||
|
set -Eeuo pipefail
|
||||||
|
cleanup() {
|
||||||
|
docker compose --env-file .env.example --profile tools down -v --remove-orphans >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
cleanup
|
||||||
|
docker compose --env-file .env.example up -d db
|
||||||
|
|
||||||
|
bootstrap_log="$(mktemp)"
|
||||||
|
set +e
|
||||||
|
docker compose --env-file .env.example --profile tools run --build --rm migrate 2>&1 | tee "$bootstrap_log"
|
||||||
|
bootstrap_status=${PIPESTATUS[0]}
|
||||||
|
set -e
|
||||||
|
if [ "$bootstrap_status" -eq 0 ]; then
|
||||||
|
echo 'ERROR: expected historical one-shot reset guard on clean database.' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -Fq 'Production reset aborted: expected exactly one username admin, found 0' "$bootstrap_log" || {
|
||||||
|
echo 'ERROR: migration chain failed before the expected historical reset guard.' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
rm -f "$bootstrap_log"
|
||||||
|
|
||||||
|
docker compose --env-file .env.example exec -T db \
|
||||||
|
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 <<'SQL'
|
||||||
|
INSERT INTO typeorm_migrations ("timestamp", name)
|
||||||
|
SELECT 1788652800000, 'ResetProductionOperationalData1788652800000'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM typeorm_migrations
|
||||||
|
WHERE name='ResetProductionOperationalData1788652800000'
|
||||||
|
);
|
||||||
|
SQL
|
||||||
|
|
||||||
|
docker compose --env-file .env.example --profile tools run --rm migrate
|
||||||
|
|
||||||
|
docker compose --env-file .env.example exec -T db \
|
||||||
|
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 <<'SQL'
|
||||||
|
DO $$
|
||||||
|
DECLARE invalid_areas integer; invalid_yacimientos integer;
|
||||||
|
BEGIN
|
||||||
|
SELECT COUNT(*) INTO invalid_areas
|
||||||
|
FROM assets area
|
||||||
|
JOIN asset_types type ON type.id=area.asset_type_id
|
||||||
|
LEFT JOIN assets department ON department.id=area.parent_id
|
||||||
|
LEFT JOIN asset_types department_type ON department_type.id=department.asset_type_id
|
||||||
|
WHERE lower(type.code)='area'
|
||||||
|
AND (department.id IS NULL OR lower(department_type.code)<>'departamento');
|
||||||
|
IF invalid_areas <> 0 THEN
|
||||||
|
RAISE EXCEPTION 'Invalid Area→Departamento relations: %', invalid_areas;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT COUNT(*) INTO invalid_yacimientos
|
||||||
|
FROM assets yacimiento
|
||||||
|
JOIN asset_types type ON type.id=yacimiento.asset_type_id
|
||||||
|
LEFT JOIN assets area ON area.id=yacimiento.parent_id
|
||||||
|
LEFT JOIN asset_types area_type ON area_type.id=area.asset_type_id
|
||||||
|
WHERE lower(type.code)='yacimiento'
|
||||||
|
AND (area.id IS NULL OR lower(area_type.code)<>'area'
|
||||||
|
OR yacimiento.operational_area_id IS DISTINCT FROM area.id);
|
||||||
|
IF invalid_yacimientos <> 0 THEN
|
||||||
|
RAISE EXCEPTION 'Invalid Yacimiento→Area relations: %', invalid_yacimientos;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
SQL
|
||||||
|
|
||||||
|
export JWT_ACCESS_SECRET='CI_ACCESS_SECRET_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||||
|
export REFRESH_TOKEN_PEPPER='CI_REFRESH_PEPPER_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
|
||||||
|
export SMTP_SETTINGS_MASTER_KEY=''
|
||||||
|
docker compose --env-file .env.example build api
|
||||||
|
|
||||||
|
# Seed two ephemeral identities only in this disposable CI database:
|
||||||
|
# one caller with planning permissions and one executable Inspector.
|
||||||
|
docker compose --env-file .env.example exec -T db \
|
||||||
|
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 <<'SQL'
|
||||||
|
INSERT INTO users(id,username,password_hash,first_name,last_name,status,must_change_password)
|
||||||
|
VALUES
|
||||||
|
('11111111-1111-4111-8111-111111111111','ci_planner','ci-unused','CI','Planner','ACTIVE',false),
|
||||||
|
('22222222-2222-4222-8222-222222222222','ci_inspector','ci-unused','CI','Inspector','ACTIVE',false);
|
||||||
|
|
||||||
|
INSERT INTO user_roles(user_id,role_id,assigned_by)
|
||||||
|
SELECT '11111111-1111-4111-8111-111111111111'::uuid, role.id,
|
||||||
|
'11111111-1111-4111-8111-111111111111'::uuid
|
||||||
|
FROM roles role
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id
|
||||||
|
WHERE rp.role_id=role.id AND p.code='inspections.manage'
|
||||||
|
)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id
|
||||||
|
WHERE rp.role_id=role.id AND p.code='inspections.assign'
|
||||||
|
)
|
||||||
|
ORDER BY role.is_system DESC,role.code
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
INSERT INTO user_roles(user_id,role_id,assigned_by)
|
||||||
|
SELECT '22222222-2222-4222-8222-222222222222'::uuid, role.id,
|
||||||
|
'11111111-1111-4111-8111-111111111111'::uuid
|
||||||
|
FROM roles role
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1 FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id
|
||||||
|
WHERE rp.role_id=role.id AND p.code='inspections.execute'
|
||||||
|
)
|
||||||
|
ORDER BY (lower(role.code)='inspector') DESC,role.is_system DESC,role.code
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM user_roles WHERE user_id='11111111-1111-4111-8111-111111111111'::uuid) THEN
|
||||||
|
RAISE EXCEPTION 'CI planner role with manage+assign permissions not found';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM user_roles WHERE user_id='22222222-2222-4222-8222-222222222222'::uuid) THEN
|
||||||
|
RAISE EXCEPTION 'CI inspector role with execute permission not found';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
INSERT INTO auth_sessions(
|
||||||
|
id,user_id,refresh_token_hash,expires_at,last_used_at,revoked_at,replaced_by_session_id,ip,user_agent,device_label
|
||||||
|
) VALUES (
|
||||||
|
'33333333-3333-4333-8333-333333333333',
|
||||||
|
'11111111-1111-4111-8111-111111111111','ci-unused',
|
||||||
|
CURRENT_TIMESTAMP + INTERVAL '10 minutes',CURRENT_TIMESTAMP,NULL,NULL,NULL,'CI','inspection-planning-smoke'
|
||||||
|
);
|
||||||
|
SQL
|
||||||
|
|
||||||
|
docker compose --env-file .env.example up -d api
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
curl -fsS http://127.0.0.1:3101/api/v3/health >/dev/null 2>&1 && break
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
curl -fsS http://127.0.0.1:3101/api/v3/health >/dev/null
|
||||||
|
|
||||||
|
TOKEN="$(docker compose --env-file .env.example exec -T api node - <<'NODE'
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
process.stdout.write(jwt.sign({
|
||||||
|
sub:'11111111-1111-4111-8111-111111111111',
|
||||||
|
sid:'33333333-3333-4333-8333-333333333333',
|
||||||
|
username:'ci_planner',typ:'access'
|
||||||
|
}, process.env.JWT_ACCESS_SECRET, {
|
||||||
|
algorithm:'HS256',expiresIn:300,issuer:'dhv2-api',audience:'dhv2'
|
||||||
|
}));
|
||||||
|
NODE
|
||||||
|
)"
|
||||||
|
|
||||||
|
CONTEXT="$(docker compose --env-file .env.example exec -T db \
|
||||||
|
psql -At -F '|' -U dhv2_owner -d dhv2 -c "
|
||||||
|
SELECT department.id,area.id,yacimiento.id,relation.company_id
|
||||||
|
FROM assets department
|
||||||
|
JOIN asset_types department_type ON department_type.id=department.asset_type_id
|
||||||
|
JOIN assets area ON area.parent_id=department.id
|
||||||
|
JOIN asset_types area_type ON area_type.id=area.asset_type_id
|
||||||
|
JOIN assets yacimiento ON yacimiento.parent_id=area.id
|
||||||
|
JOIN asset_types yacimiento_type ON yacimiento_type.id=yacimiento.asset_type_id
|
||||||
|
JOIN area_company_relations relation ON relation.area_id=area.id
|
||||||
|
AND relation.relation_role='OPERATOR' AND relation.valid_until IS NULL
|
||||||
|
WHERE lower(department_type.code)='departamento'
|
||||||
|
AND lower(area_type.code)='area'
|
||||||
|
AND lower(yacimiento_type.code)='yacimiento'
|
||||||
|
ORDER BY department.name,area.name,yacimiento.name
|
||||||
|
LIMIT 1;
|
||||||
|
")"
|
||||||
|
IFS='|' read -r DEPARTMENT_ID AREA_ID YACIMIENTO_ID COMPANY_ID <<< "$CONTEXT"
|
||||||
|
test -n "$DEPARTMENT_ID" && test -n "$AREA_ID" && test -n "$YACIMIENTO_ID" && test -n "$COMPANY_ID"
|
||||||
|
|
||||||
|
curl -fsS -H "Authorization: Bearer $TOKEN" \
|
||||||
|
http://127.0.0.1:3101/api/v3/inspection-visits/planning-context/departments \
|
||||||
|
>/tmp/dhv2-departments.json
|
||||||
|
grep -Fq "$DEPARTMENT_ID" /tmp/dhv2-departments.json
|
||||||
|
|
||||||
|
curl -fsS -H "Authorization: Bearer $TOKEN" \
|
||||||
|
"http://127.0.0.1:3101/api/v3/inspection-visits/planning-context/departments/$DEPARTMENT_ID/areas" \
|
||||||
|
>/tmp/dhv2-areas.json
|
||||||
|
grep -Fq "$AREA_ID" /tmp/dhv2-areas.json
|
||||||
|
|
||||||
|
curl -fsS -H "Authorization: Bearer $TOKEN" \
|
||||||
|
"http://127.0.0.1:3101/api/v3/inspection-visits/planning-context/areas/$AREA_ID/yacimientos" \
|
||||||
|
>/tmp/dhv2-yacimientos.json
|
||||||
|
grep -Fq "$YACIMIENTO_ID" /tmp/dhv2-yacimientos.json
|
||||||
|
|
||||||
|
START_AT="$(date -u -d '+1 day' +'%Y-%m-%dT%H:%M:%S.000Z')"
|
||||||
|
HTTP_CODE="$(curl -sS -o /tmp/dhv2-created.json -w '%{http_code}' \
|
||||||
|
-X POST \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"operationalAreaId\":\"$AREA_ID\",\"scopeAssetId\":\"$YACIMIENTO_ID\",\"operatorCompanyId\":\"$COMPANY_ID\",\"plannedStartAt\":\"$START_AT\",\"leadInspectorUserId\":\"22222222-2222-4222-8222-222222222222\"}" \
|
||||||
|
http://127.0.0.1:3101/api/v3/inspection-visits)"
|
||||||
|
if [ "$HTTP_CODE" != 201 ]; then
|
||||||
|
echo "ERROR: real inspection create returned HTTP $HTTP_CODE" >&2
|
||||||
|
cat /tmp/dhv2-created.json >&2
|
||||||
|
docker compose --env-file .env.example logs --no-color api >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VISIT_ID="$(python - <<'PY'
|
||||||
|
import json
|
||||||
|
with open('/tmp/dhv2-created.json',encoding='utf-8') as f:
|
||||||
|
print(json.load(f)['id'])
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
|
||||||
|
docker compose --env-file .env.example exec -T db \
|
||||||
|
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 \
|
||||||
|
-v visit_id="$VISIT_ID" -v area_id="$AREA_ID" -v yacimiento_id="$YACIMIENTO_ID" -v company_id="$COMPANY_ID" <<'SQL'
|
||||||
|
DO $$
|
||||||
|
DECLARE ok integer;
|
||||||
|
BEGIN
|
||||||
|
SELECT COUNT(*) INTO ok
|
||||||
|
FROM inspection_visits visit
|
||||||
|
JOIN assets yacimiento ON yacimiento.id=visit.scope_asset_id
|
||||||
|
JOIN asset_types yacimiento_type ON yacimiento_type.id=yacimiento.asset_type_id
|
||||||
|
WHERE visit.id=:'visit_id'::uuid
|
||||||
|
AND visit.operational_area_id=:'area_id'::uuid
|
||||||
|
AND visit.scope_asset_id=:'yacimiento_id'::uuid
|
||||||
|
AND visit.operator_company_id=:'company_id'::uuid
|
||||||
|
AND yacimiento.parent_id=visit.operational_area_id
|
||||||
|
AND lower(yacimiento_type.code)='yacimiento';
|
||||||
|
IF ok <> 1 THEN
|
||||||
|
RAISE EXCEPTION 'Created Inspection did not preserve Area/Yacimiento hierarchy';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
SQL
|
||||||
|
|
||||||
|
echo "F6.1 real inspection create smoke: OK ($VISIT_ID)"
|
||||||
Reference in New Issue
Block a user