chore: crush git history - reborn from consolidation on 2026-03-10

This commit is contained in:
John Dvorak
2026-03-10 00:00:00 -07:00
commit d278c4b105
313 changed files with 87549 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
version: 2.1
orbs:
node: circleci/node@5
jobs:
contract-tests:
docker:
- image: cimg/node:20.0
steps:
- checkout
- node/install-packages:
pkg-manager: npm
- restore_cache:
keys:
- apophis-cache-{{ .Branch }}
- apophis-cache-main
- run:
name: Determine changed routes
command: |
# Extract changed routes from git diff
CHANGED=$(git diff --name-only HEAD~1 HEAD | \
grep -E 'src/routes|src/schemas' | \
sed 's|src/routes||; s|\.ts$||' | \
paste -sd ',' -)
echo "export APOPHIS_CHANGED_ROUTES=$CHANGED" >> $BASH_ENV
- run:
name: Run contract tests
command: npm test
environment:
APOPHIS_LOG_LEVEL: info
- save_cache:
paths:
- .apophis-cache.json
key: apophis-cache-{{ .Branch }}-{{ epoch }}
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: test-results
workflows:
test:
jobs:
- contract-tests
+164
View File
@@ -0,0 +1,164 @@
import Fastify from 'fastify'
import apophisPlugin from 'apophis-fastify'
const fastify = Fastify()
await fastify.register(apophisPlugin, {
runtime: 'error', // Validate contracts on every request
cleanup: true, // Auto-cleanup resources on exit
})
// In-memory store for demo
const users = new Map<string, { id: string; email: string; name: string }>()
// CREATE — constructor
fastify.post('/users', {
schema: {
'x-category': 'constructor',
'x-ensures': [
'status:201',
'response_body(this).id != null',
'response_body(this).email == request_body(this).email',
],
body: {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
name: { type: 'string', minLength: 1 }
},
required: ['email', 'name']
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'string' },
email: { type: 'string' },
name: { type: 'string' }
}
}
}
}
}, async (req, reply) => {
const id = `usr-${Date.now()}`
const user = { id, email: req.body.email, name: req.body.name }
users.set(id, user)
reply.status(201)
return user
})
// READ — observer
fastify.get('/users/:id', {
schema: {
'x-category': 'observer',
'x-requires': ['users:id'],
'x-ensures': [
'status:200',
'response_body(this).id == request_params(this).id',
],
params: {
type: 'object',
properties: {
id: { type: 'string' }
},
required: ['id']
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'string' },
email: { type: 'string' },
name: { type: 'string' }
}
}
}
}
}, async (req) => {
const user = users.get(req.params.id)
if (!user) {
throw new Error('User not found')
}
return user
})
// UPDATE — mutator
fastify.put('/users/:id', {
schema: {
'x-category': 'mutator',
'x-requires': ['users:id'],
'x-ensures': [
'status:200',
'response_body(this).id == request_params(this).id',
],
params: {
type: 'object',
properties: {
id: { type: 'string' }
},
required: ['id']
},
body: {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
name: { type: 'string', minLength: 1 }
}
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'string' },
email: { type: 'string' },
name: { type: 'string' }
}
}
}
}
}, async (req) => {
const user = users.get(req.params.id)
if (!user) {
throw new Error('User not found')
}
const updated = {
...user,
email: req.body.email ?? user.email,
name: req.body.name ?? user.name,
}
users.set(req.params.id, updated)
return updated
})
// DELETE — destructor
fastify.delete('/users/:id', {
schema: {
'x-category': 'destructor',
'x-requires': ['users:id'],
'x-ensures': ['status:204'],
params: {
type: 'object',
properties: {
id: { type: 'string' }
},
required: ['id']
}
}
}, async (req, reply) => {
users.delete(req.params.id)
reply.status(204)
})
await fastify.ready()
// Run contract tests (all non-utility routes, property-based)
const result = await fastify.apophis.contract({ depth: 'standard' })
console.log('Contract tests:', result.summary)
// Run stateful tests (constructor→mutator→destructor sequences)
const stateful = await fastify.apophis.stateful({ depth: 'standard', seed: 42 })
console.log('Stateful tests:', stateful.summary)
// Validate a single route
const check = await fastify.apophis.check('POST', '/users')
console.log('POST /users check:', check.ok ? 'PASS' : 'FAIL')
+63
View File
@@ -0,0 +1,63 @@
name: API Contract Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Restore APOPHIS cache
uses: actions/cache@v4
with:
path: .apophis-cache.json
key: apophis-cache-${{ github.ref }}-${{ github.sha }}
restore-keys: |
apophis-cache-${{ github.ref }}-
apophis-cache-main-
- name: Determine changed routes
id: changed
run: |
# Example: extract changed routes from git diff
# Adjust this to match your project's structure
CHANGED=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | \
grep -E 'src/routes|src/schemas' | \
sed 's|src/routes||; s|\.ts$||' | \
paste -sd ',' -)
echo "routes=$CHANGED" >> $GITHUB_OUTPUT
- name: Run contract tests
run: npm test
env:
APOPHIS_LOG_LEVEL: info
APOPHIS_CHANGED_ROUTES: ${{ steps.changed.outputs.routes }}
- name: Save APOPHIS cache
uses: actions/cache@v4
with:
path: .apophis-cache.json
key: apophis-cache-${{ github.ref }}-${{ github.sha }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: |
*.tap
.apophis-cache.json
+50
View File
@@ -0,0 +1,50 @@
stages:
- test
- deploy
variables:
NODE_VERSION: "20"
APOPHIS_LOG_LEVEL: "info"
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .apophis-cache.json
contract_tests:
stage: test
image: node:${NODE_VERSION}
before_script:
- npm ci
script:
# Determine changed routes from merge request diff
- |
if [ "$CI_MERGE_REQUEST_IID" != "" ]; then
CHANGED=$(git diff --name-only $CI_MERGE_REQUEST_DIFF_BASE_SHA $CI_COMMIT_SHA | \
grep -E 'src/routes|src/schemas' | \
sed 's|src/routes||; s|\.ts$||' | \
paste -sd ',' -)
export APOPHIS_CHANGED_ROUTES="$CHANGED"
fi
- npm test
artifacts:
when: always
paths:
- .apophis-cache.json
- "*.tap"
reports:
junit: junit.xml
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == "main"
# Optional: Clear cache after deployment to main
clear_cache:
stage: deploy
image: node:${NODE_VERSION}
script:
- rm -f .apophis-cache.json
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: on_success
+26
View File
@@ -0,0 +1,26 @@
import Fastify from 'fastify'
import apophisPlugin from 'apophis-fastify'
const fastify = Fastify()
// APOPHIS auto-registers @fastify/swagger
await fastify.register(apophisPlugin, {})
fastify.get('/health', {
schema: {
'x-category': 'observer',
'x-ensures': ['status:200'],
response: {
200: {
type: 'object',
properties: { status: { type: 'string' } }
}
}
}
}, async () => ({ status: 'ok' }))
await fastify.ready()
// Run contract tests
const result = await fastify.apophis.contract({ depth: 'quick' })
console.log(result.summary)