-
Notifications
You must be signed in to change notification settings - Fork 55
131 lines (116 loc) · 4.26 KB
/
Copy pathapi-contract.yml
File metadata and controls
131 lines (116 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
name: API contract
on:
pull_request:
paths:
- 'docs/openapi.yaml'
- 'src/routes/**'
- '.github/workflows/api-contract.yml'
push:
branches: [main]
paths:
- 'docs/openapi.yaml'
jobs:
validate-spec:
name: Validate OpenAPI spec
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install Redocly CLI
run: npm install -g @redocly/cli@latest
- name: Lint OpenAPI spec
run: redocly lint docs/openapi.yaml --format=stylish
- name: Bundle spec (verify all $refs resolve)
run: redocly bundle docs/openapi.yaml --output /tmp/openapi-bundled.yaml
contract-tests:
name: Contract smoke tests
runs-on: ubuntu-latest
needs: validate-spec
services:
postgres:
image: postgres:14.4
env:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: db
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U user"
--health-interval=10s
--health-timeout=5s
--health-retries=5
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Prisma generate
run: npx prisma generate
env:
DATABASE_URL: postgresql://user:pass@localhost:5432/db
- name: Prisma migrate deploy
run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://user:pass@localhost:5432/db
- name: Start server
run: |
cp .env.test .env
npm run build
node dist/index.js > /tmp/server.log 2>&1 &
echo $! > /tmp/server.pid
# initServices() boots the DB, event listener, and agent loop before
# app.listen(), so allow generous time — and fail loudly with the
# server log rather than letting a later step report a bare failure.
for i in $(seq 1 60); do
if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
echo "Server ready after ${i}s"
exit 0
fi
if ! kill -0 "$(cat /tmp/server.pid)" 2>/dev/null; then
echo "::error::Server process exited during startup"
cat /tmp/server.log
exit 1
fi
sleep 1
done
echo "::error::Server did not become ready within 60s"
cat /tmp/server.log
exit 1
env:
NODE_ENV: test
# This job boots the real server, and startEventListener() calls
# getLatestLedger() with no client timeout before app.listen(). Point
# it at a reachable RPC (as production-smoke.yml does) so an
# unresolvable host cannot stall startup. dotenv does not override
# ambient vars, so this wins over .env.test.
STELLAR_RPC_URL: https://soroban-testnet.stellar.org
- name: Health liveness check
run: |
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" http://localhost:3000/health)
[ "$STATUS" = "200" ] || (echo "Health check returned $STATUS" && exit 1)
- name: Validate health response shape
run: |
BODY=$(curl -sf http://localhost:3000/health)
echo "$BODY" | node -e "
const body = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
const required = ['status','timestamp','version','environment'];
for (const k of required) {
if (!(k in body)) { console.error('Missing field: ' + k); process.exit(1); }
}
console.log('Health response shape OK:', JSON.stringify(body));
"
- name: 401 on protected routes without token
run: |
for ROUTE in /api/portfolio /api/transactions /api/vault; do
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" http://localhost:3000$ROUTE || true)
[ "$STATUS" = "401" ] || [ "$STATUS" = "404" ] || (echo "$ROUTE returned $STATUS, expected 401" && exit 1)
echo "$ROUTE -> $STATUS OK"
done