@kirrosh/apitool 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +27 -0
- package/.github/workflows/release.yml +97 -0
- package/.mcp.json +9 -0
- package/APITOOL.md +195 -0
- package/BACKLOG.md +62 -0
- package/CHANGELOG.md +88 -0
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/bun.lock +291 -0
- package/docs/GLOSSARY.md +182 -0
- package/docs/INDEX.md +21 -0
- package/docs/agent.md +135 -0
- package/docs/archive/APITOOL-pre-M22.md +831 -0
- package/docs/archive/BACKLOG-AI-NATIVE.md +56 -0
- package/docs/archive/M1-M2-parser-runner.md +216 -0
- package/docs/archive/M4-M7-reporter-cli.md +179 -0
- package/docs/archive/M5-M7-storage-junit.md +300 -0
- package/docs/archive/M6-webui.md +339 -0
- package/docs/ci.md +274 -0
- package/docs/generation-issues.md +67 -0
- package/generated/.env.yaml +3 -0
- package/install.ps1 +80 -0
- package/install.sh +113 -0
- package/package.json +46 -0
- package/scripts/run-mocked-tests.ts +45 -0
- package/seed-demo.ts +53 -0
- package/self-tests/auth.yaml +18 -0
- package/self-tests/collections-crud.yaml +46 -0
- package/self-tests/environments-crud.yaml +48 -0
- package/self-tests/export.yaml +32 -0
- package/self-tests/runs.yaml +16 -0
- package/src/bun-types.d.ts +5 -0
- package/src/cli/commands/add-api.ts +51 -0
- package/src/cli/commands/ai-generate.ts +106 -0
- package/src/cli/commands/chat.ts +43 -0
- package/src/cli/commands/ci-init.ts +126 -0
- package/src/cli/commands/collections.ts +41 -0
- package/src/cli/commands/coverage.ts +65 -0
- package/src/cli/commands/doctor.ts +127 -0
- package/src/cli/commands/envs.ts +218 -0
- package/src/cli/commands/init.ts +84 -0
- package/src/cli/commands/mcp.ts +16 -0
- package/src/cli/commands/run.ts +137 -0
- package/src/cli/commands/runs.ts +108 -0
- package/src/cli/commands/serve.ts +22 -0
- package/src/cli/commands/update.ts +142 -0
- package/src/cli/commands/validate.ts +18 -0
- package/src/cli/index.ts +500 -0
- package/src/cli/output.ts +24 -0
- package/src/cli/runtime.ts +7 -0
- package/src/core/agent/agent-loop.ts +116 -0
- package/src/core/agent/context-manager.ts +41 -0
- package/src/core/agent/system-prompt.ts +33 -0
- package/src/core/agent/tools/diagnose-failure.ts +51 -0
- package/src/core/agent/tools/explore-api.ts +40 -0
- package/src/core/agent/tools/index.ts +48 -0
- package/src/core/agent/tools/manage-environment.ts +40 -0
- package/src/core/agent/tools/query-results.ts +40 -0
- package/src/core/agent/tools/run-tests.ts +38 -0
- package/src/core/agent/tools/send-request.ts +44 -0
- package/src/core/agent/tools/validate-tests.ts +23 -0
- package/src/core/agent/types.ts +22 -0
- package/src/core/generator/ai/ai-generator.ts +61 -0
- package/src/core/generator/ai/llm-client.ts +159 -0
- package/src/core/generator/ai/output-parser.ts +307 -0
- package/src/core/generator/ai/prompt-builder.ts +153 -0
- package/src/core/generator/ai/types.ts +56 -0
- package/src/core/generator/coverage-scanner.ts +87 -0
- package/src/core/generator/data-factory.ts +115 -0
- package/src/core/generator/index.ts +10 -0
- package/src/core/generator/openapi-reader.ts +142 -0
- package/src/core/generator/schema-utils.ts +52 -0
- package/src/core/generator/serializer.ts +189 -0
- package/src/core/generator/types.ts +47 -0
- package/src/core/parser/filter.ts +14 -0
- package/src/core/parser/index.ts +21 -0
- package/src/core/parser/schema.ts +175 -0
- package/src/core/parser/types.ts +50 -0
- package/src/core/parser/variables.ts +146 -0
- package/src/core/parser/yaml-parser.ts +85 -0
- package/src/core/reporter/console.ts +175 -0
- package/src/core/reporter/index.ts +23 -0
- package/src/core/reporter/json.ts +9 -0
- package/src/core/reporter/junit.ts +78 -0
- package/src/core/reporter/types.ts +12 -0
- package/src/core/runner/assertions.ts +172 -0
- package/src/core/runner/execute-run.ts +75 -0
- package/src/core/runner/executor.ts +150 -0
- package/src/core/runner/http-client.ts +69 -0
- package/src/core/runner/index.ts +12 -0
- package/src/core/runner/types.ts +48 -0
- package/src/core/setup-api.ts +97 -0
- package/src/core/utils.ts +9 -0
- package/src/db/queries.ts +868 -0
- package/src/db/schema.ts +215 -0
- package/src/mcp/server.ts +47 -0
- package/src/mcp/tools/ci-init.ts +57 -0
- package/src/mcp/tools/coverage-analysis.ts +58 -0
- package/src/mcp/tools/explore-api.ts +84 -0
- package/src/mcp/tools/generate-missing-tests.ts +80 -0
- package/src/mcp/tools/generate-tests-guide.ts +353 -0
- package/src/mcp/tools/manage-environment.ts +123 -0
- package/src/mcp/tools/manage-server.ts +87 -0
- package/src/mcp/tools/query-db.ts +141 -0
- package/src/mcp/tools/run-tests.ts +66 -0
- package/src/mcp/tools/save-test-suite.ts +164 -0
- package/src/mcp/tools/send-request.ts +53 -0
- package/src/mcp/tools/setup-api.ts +49 -0
- package/src/mcp/tools/validate-tests.ts +42 -0
- package/src/tui/chat-ui.ts +150 -0
- package/src/web/routes/api.ts +234 -0
- package/src/web/routes/dashboard.ts +348 -0
- package/src/web/routes/runs.ts +64 -0
- package/src/web/schemas.ts +121 -0
- package/src/web/server.ts +134 -0
- package/src/web/static/htmx.min.js +1 -0
- package/src/web/static/style.css +265 -0
- package/src/web/views/layout.ts +46 -0
- package/src/web/views/results.ts +209 -0
- package/tests/agent/agent-loop.test.ts +61 -0
- package/tests/agent/context-manager.test.ts +59 -0
- package/tests/agent/system-prompt.test.ts +42 -0
- package/tests/agent/tools/diagnose-failure.test.ts +85 -0
- package/tests/agent/tools/explore-api.test.ts +59 -0
- package/tests/agent/tools/manage-environment.test.ts +78 -0
- package/tests/agent/tools/query-results.test.ts +77 -0
- package/tests/agent/tools/run-tests.test.ts +89 -0
- package/tests/agent/tools/send-request.test.ts +78 -0
- package/tests/agent/tools/validate-tests.test.ts +59 -0
- package/tests/ai/ai-generator.integration.test.ts +131 -0
- package/tests/ai/llm-client.test.ts +145 -0
- package/tests/ai/output-parser.test.ts +132 -0
- package/tests/ai/prompt-builder.test.ts +67 -0
- package/tests/ai/types.test.ts +55 -0
- package/tests/cli/args.test.ts +63 -0
- package/tests/cli/chat.test.ts +38 -0
- package/tests/cli/ci-init.test.ts +112 -0
- package/tests/cli/commands.test.ts +316 -0
- package/tests/cli/coverage.test.ts +58 -0
- package/tests/cli/doctor.test.ts +39 -0
- package/tests/cli/envs.test.ts +181 -0
- package/tests/cli/init.test.ts +80 -0
- package/tests/cli/runs.test.ts +94 -0
- package/tests/cli/safe-run.test.ts +103 -0
- package/tests/cli/update.test.ts +32 -0
- package/tests/core/generator/schema-utils.test.ts +108 -0
- package/tests/core/parser/nested-assertions.test.ts +80 -0
- package/tests/core/runner/root-body-assertions.test.ts +70 -0
- package/tests/db/chat-queries.test.ts +88 -0
- package/tests/db/chat-schema.test.ts +37 -0
- package/tests/db/environments.test.ts +131 -0
- package/tests/db/queries.test.ts +409 -0
- package/tests/db/schema.test.ts +141 -0
- package/tests/fixtures/.env.yaml +3 -0
- package/tests/fixtures/auth-token-test.yaml +8 -0
- package/tests/fixtures/bail/suite-a.yaml +6 -0
- package/tests/fixtures/bail/suite-b.yaml +6 -0
- package/tests/fixtures/crud.yaml +35 -0
- package/tests/fixtures/invalid-missing-name.yaml +5 -0
- package/tests/fixtures/invalid-no-method.yaml +6 -0
- package/tests/fixtures/petstore-auth.json +295 -0
- package/tests/fixtures/petstore-simple.json +151 -0
- package/tests/fixtures/post-only.yaml +12 -0
- package/tests/fixtures/simple.yaml +6 -0
- package/tests/fixtures/valid/.env.yaml +1 -0
- package/tests/fixtures/valid/a.yaml +5 -0
- package/tests/fixtures/valid/b.yml +5 -0
- package/tests/generator/coverage-scanner.test.ts +129 -0
- package/tests/generator/data-factory.test.ts +133 -0
- package/tests/generator/openapi-reader.test.ts +131 -0
- package/tests/integration/auth-flow.test.ts +217 -0
- package/tests/mcp/coverage-analysis.test.ts +64 -0
- package/tests/mcp/explore-api-schemas.test.ts +105 -0
- package/tests/mcp/explore-api.test.ts +49 -0
- package/tests/mcp/generate-missing-tests.test.ts +69 -0
- package/tests/mcp/manage-environment.test.ts +89 -0
- package/tests/mcp/save-test-suite.test.ts +116 -0
- package/tests/mcp/send-request.test.ts +79 -0
- package/tests/mcp/setup-api.test.ts +106 -0
- package/tests/mcp/tools.test.ts +248 -0
- package/tests/parser/schema.test.ts +134 -0
- package/tests/parser/variables.test.ts +227 -0
- package/tests/parser/yaml-parser.test.ts +69 -0
- package/tests/reporter/console.test.ts +256 -0
- package/tests/reporter/json.test.ts +98 -0
- package/tests/reporter/junit.test.ts +284 -0
- package/tests/runner/assertions.test.ts +262 -0
- package/tests/runner/executor.test.ts +310 -0
- package/tests/runner/http-client.test.ts +138 -0
- package/tests/web/routes.test.ts +160 -0
- package/tsconfig.json +31 -0
package/docs/ci.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# CI/CD Integration
|
|
2
|
+
|
|
3
|
+
Run apitool API tests automatically in your CI/CD pipeline.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Generate CI workflow for your project
|
|
9
|
+
apitool ci init # auto-detect platform
|
|
10
|
+
apitool ci init --github # GitHub Actions
|
|
11
|
+
apitool ci init --gitlab # GitLab CI
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## GitHub Actions
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
name: API Tests
|
|
18
|
+
on:
|
|
19
|
+
push:
|
|
20
|
+
branches: [main]
|
|
21
|
+
pull_request:
|
|
22
|
+
schedule:
|
|
23
|
+
- cron: "0 */6 * * *"
|
|
24
|
+
workflow_dispatch:
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
checks: write
|
|
29
|
+
pull-requests: write
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
test:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- name: Install apitool
|
|
38
|
+
run: curl -fsSL https://raw.githubusercontent.com/kirrosh/apitool/master/install.sh | sh
|
|
39
|
+
|
|
40
|
+
- name: Run tests
|
|
41
|
+
run: |
|
|
42
|
+
mkdir -p test-results
|
|
43
|
+
apitool run apis/ --report junit --no-db > test-results/junit.xml
|
|
44
|
+
continue-on-error: true
|
|
45
|
+
|
|
46
|
+
- name: Publish test results
|
|
47
|
+
uses: EnricoMi/publish-unit-test-result-action@v2
|
|
48
|
+
if: always()
|
|
49
|
+
with:
|
|
50
|
+
files: test-results/junit.xml
|
|
51
|
+
|
|
52
|
+
- uses: actions/upload-artifact@v4
|
|
53
|
+
if: always()
|
|
54
|
+
with:
|
|
55
|
+
name: test-results
|
|
56
|
+
path: test-results/junit.xml
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
> **Note:** `continue-on-error: true` ensures junit.xml is always written and published, even when tests fail (exit code 1). The `publish-unit-test-result-action` will set the check status based on test results.
|
|
60
|
+
|
|
61
|
+
## GitLab CI
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
api-tests:
|
|
65
|
+
image: ubuntu:latest
|
|
66
|
+
before_script:
|
|
67
|
+
- apt-get update -qq && apt-get install -y -qq curl
|
|
68
|
+
- curl -fsSL https://raw.githubusercontent.com/kirrosh/apitool/master/install.sh | sh
|
|
69
|
+
script:
|
|
70
|
+
- mkdir -p test-results
|
|
71
|
+
- apitool run apis/ --report junit --no-db > test-results/junit.xml
|
|
72
|
+
allow_failure:
|
|
73
|
+
exit_codes: 1
|
|
74
|
+
artifacts:
|
|
75
|
+
when: always
|
|
76
|
+
reports:
|
|
77
|
+
junit: test-results/junit.xml
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Jenkins
|
|
81
|
+
|
|
82
|
+
```groovy
|
|
83
|
+
pipeline {
|
|
84
|
+
agent any
|
|
85
|
+
|
|
86
|
+
stages {
|
|
87
|
+
stage('Install') {
|
|
88
|
+
steps {
|
|
89
|
+
sh 'curl -fsSL https://raw.githubusercontent.com/kirrosh/apitool/master/install.sh | sh'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
stage('Test') {
|
|
93
|
+
steps {
|
|
94
|
+
sh 'mkdir -p test-results'
|
|
95
|
+
sh 'apitool run apis/ --report junit --no-db > test-results/junit.xml || true'
|
|
96
|
+
}
|
|
97
|
+
post {
|
|
98
|
+
always {
|
|
99
|
+
junit 'test-results/junit.xml'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Generic Shell Script
|
|
108
|
+
|
|
109
|
+
Works with any CI system (CircleCI, Travis, Drone, etc.):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
#!/bin/bash
|
|
113
|
+
set -uo pipefail
|
|
114
|
+
|
|
115
|
+
# Install apitool
|
|
116
|
+
curl -fsSL https://raw.githubusercontent.com/kirrosh/apitool/master/install.sh | sh
|
|
117
|
+
|
|
118
|
+
# Run tests with JUnit output
|
|
119
|
+
mkdir -p test-results
|
|
120
|
+
apitool run apis/ --report junit --no-db > test-results/junit.xml
|
|
121
|
+
EXIT_CODE=$?
|
|
122
|
+
|
|
123
|
+
# Exit code: 0 = all passed, 1 = failures, 2 = error
|
|
124
|
+
exit $EXIT_CODE
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Environment Variables
|
|
128
|
+
|
|
129
|
+
`--env <name>` loads `.env.<name>.yaml` from the **test path directory** (`dirname` of the path passed to `apitool run`).
|
|
130
|
+
|
|
131
|
+
For example:
|
|
132
|
+
- `apitool run apis/petstore/tests/ --env ci` → looks for `apis/petstore/tests/.env.ci.yaml`
|
|
133
|
+
- `apitool run apis/ --env ci` → looks for `.env.ci.yaml` in current directory (parent of `apis/`)
|
|
134
|
+
|
|
135
|
+
If your env files live next to test files in subdirectories, run each API separately:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
apitool run apis/petstore/tests/ --env default --report junit --no-db
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Or place a `.env.yaml` (no name) in the repo root for shared variables.
|
|
142
|
+
|
|
143
|
+
### Secrets in CI
|
|
144
|
+
|
|
145
|
+
Pass secrets as environment variables and reference them in `.env.ci.yaml`:
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
# .env.ci.yaml (in repo root or test directory)
|
|
149
|
+
base_url: https://api.staging.example.com
|
|
150
|
+
api_key: ${{ API_KEY }}
|
|
151
|
+
auth_token: ${{ AUTH_TOKEN }}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### GitHub Actions
|
|
155
|
+
|
|
156
|
+
```yaml
|
|
157
|
+
- name: Run tests
|
|
158
|
+
env:
|
|
159
|
+
API_KEY: ${{ secrets.API_KEY }}
|
|
160
|
+
AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
|
|
161
|
+
run: apitool run apis/ --env ci --report junit --no-db > test-results/junit.xml
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### GitLab CI
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
api-tests:
|
|
168
|
+
variables:
|
|
169
|
+
API_KEY: $API_KEY # Set in GitLab CI/CD settings
|
|
170
|
+
script:
|
|
171
|
+
- apitool run apis/ --env ci --report junit --no-db > test-results/junit.xml
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Auth token shortcut
|
|
175
|
+
|
|
176
|
+
For simple bearer token auth, use `--auth-token` instead of an env file:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
apitool run apis/ --auth-token "$AUTH_TOKEN" --report junit --no-db
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Triggers
|
|
183
|
+
|
|
184
|
+
The generated workflow runs on push, PR, schedule, and manual dispatch by default. You can also trigger tests from external events.
|
|
185
|
+
|
|
186
|
+
### Schedule (cron)
|
|
187
|
+
|
|
188
|
+
Already included in the template. Adjust the cron expression:
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
schedule:
|
|
192
|
+
- cron: "0 */6 * * *" # every 6 hours
|
|
193
|
+
- cron: "0 9 * * 1-5" # weekdays at 9am UTC
|
|
194
|
+
- cron: "*/30 * * * *" # every 30 minutes
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Trigger from another repo (GitHub)
|
|
198
|
+
|
|
199
|
+
Use `repository_dispatch` to trigger API tests when your backend repo deploys:
|
|
200
|
+
|
|
201
|
+
**In the test repo workflow** (already included in template):
|
|
202
|
+
```yaml
|
|
203
|
+
on:
|
|
204
|
+
repository_dispatch:
|
|
205
|
+
types: [api-updated]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**In the backend repo** — add a step after deploy:
|
|
209
|
+
```yaml
|
|
210
|
+
# .github/workflows/deploy.yml (backend repo)
|
|
211
|
+
- name: Trigger API tests
|
|
212
|
+
run: |
|
|
213
|
+
curl -X POST \
|
|
214
|
+
-H "Authorization: token ${{ secrets.TEST_REPO_PAT }}" \
|
|
215
|
+
-H "Accept: application/vnd.github.v3+json" \
|
|
216
|
+
https://api.github.com/repos/OWNER/apitool-tests/dispatches \
|
|
217
|
+
-d '{"event_type": "api-updated", "client_payload": {"env": "staging"}}'
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Or with `gh` CLI:
|
|
221
|
+
```bash
|
|
222
|
+
gh api repos/OWNER/apitool-tests/dispatches \
|
|
223
|
+
-f event_type=api-updated \
|
|
224
|
+
-f 'client_payload[env]=staging'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
> **Note:** Requires a Personal Access Token (PAT) with `repo` scope stored as a secret in the backend repo.
|
|
228
|
+
|
|
229
|
+
### Trigger from another repo (GitLab)
|
|
230
|
+
|
|
231
|
+
Use GitLab pipeline triggers:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
curl -X POST \
|
|
235
|
+
--form "ref=main" \
|
|
236
|
+
--form "token=$TRIGGER_TOKEN" \
|
|
237
|
+
"https://gitlab.com/api/v4/projects/PROJECT_ID/trigger/pipeline"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Add the trigger token in GitLab: Settings → CI/CD → Pipeline triggers.
|
|
241
|
+
|
|
242
|
+
### Webhook from external service
|
|
243
|
+
|
|
244
|
+
Any service that can send HTTP POST requests can trigger tests:
|
|
245
|
+
|
|
246
|
+
**GitHub:** Use `repository_dispatch` (see above)
|
|
247
|
+
|
|
248
|
+
**GitLab:** Use pipeline trigger tokens
|
|
249
|
+
|
|
250
|
+
**Generic (any CI):** Use the CI platform's API to trigger a build. Example with GitHub CLI:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
gh workflow run api-tests.yml --repo OWNER/apitool-tests
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Exit Codes
|
|
257
|
+
|
|
258
|
+
| Code | Meaning |
|
|
259
|
+
|------|---------|
|
|
260
|
+
| 0 | All tests passed |
|
|
261
|
+
| 1 | One or more tests failed |
|
|
262
|
+
| 2 | Configuration or runtime error |
|
|
263
|
+
|
|
264
|
+
## Key Flags for CI
|
|
265
|
+
|
|
266
|
+
| Flag | Description |
|
|
267
|
+
|------|-------------|
|
|
268
|
+
| `--report junit` | Output JUnit XML for CI integration |
|
|
269
|
+
| `--no-db` | Skip writing to local SQLite database |
|
|
270
|
+
| `--env <name>` | Load `.env.<name>.yaml` from test path directory |
|
|
271
|
+
| `--bail` | Stop on first suite failure |
|
|
272
|
+
| `--safe` | Run only GET tests (read-only mode) |
|
|
273
|
+
| `--tag <tag>` | Filter suites by tag |
|
|
274
|
+
| `--auth-token <token>` | Inject bearer token as `{{auth_token}}` |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Проблемы при первичной генерации тестов
|
|
2
|
+
|
|
3
|
+
## 1. `writeSuites` возвращал пустой массив при incremental generation
|
|
4
|
+
|
|
5
|
+
**Проблема:** Функция `writeSuites` возвращала только новые файлы. При повторной генерации (incremental mode) существующие файлы пропускались и не попадали в результат — MCP-инструмент `generate_tests` возвращал `files: []`, хотя файлы на диске есть.
|
|
6
|
+
|
|
7
|
+
**Исправление:** `writeSuites` теперь возвращает `{ written: string[], skipped: string[] }`. MCP и CLI используют оба списка для полного отчёта.
|
|
8
|
+
|
|
9
|
+
**Файлы:** `src/core/generator/serializer.ts` (ранее skeleton.ts)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 2. `getDb()` не восстанавливался после удаления файла БД
|
|
14
|
+
|
|
15
|
+
**Проблема:** Синглтон `_db` кешировал соединение. Если файл `apitool.db` удалялся (при пересоздании проекта), последующие вызовы `getDb()` возвращали "disk I/O error" — соединение было протухшим.
|
|
16
|
+
|
|
17
|
+
**Исправление:** `getDb()` теперь проверяет `existsSync(path)` перед возвратом кешированного соединения. Если файл удалён — соединение пересоздаётся.
|
|
18
|
+
|
|
19
|
+
**Файлы:** `src/db/schema.ts`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 3. MCP `generate_tests` не создавал коллекцию и окружение в БД
|
|
24
|
+
|
|
25
|
+
**Проблема:** Блок создания коллекции/окружения падал в `catch` из-за проблемы #2 (протухшее DB-соединение). Ошибка тихо проглатывалась.
|
|
26
|
+
|
|
27
|
+
**Исправление:** Исправлен `getDb()` (проблема #2). Добавлено логирование ошибок в `catch` вместо тихого игнорирования.
|
|
28
|
+
|
|
29
|
+
**Примечание:** MCP tool `generate_tests` удалён в M21. AI-генерация доступна через `apitool ai-generate` и WebUI.
|
|
30
|
+
|
|
31
|
+
**Файлы:** `src/db/schema.ts`
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 4. Относительный `base_url` из спецификации ломал Explorer и тесты
|
|
36
|
+
|
|
37
|
+
**Проблема:** Swagger-спецификация содержит `servers[0].url = "/docgen2/docgen-ui-service/"` — относительный URL. При Try it в Explorer этот URL конкатенировался с path, давая невалидный URL для `fetch()`. В тестах (runner) аналогично — URL без хоста невалиден.
|
|
38
|
+
|
|
39
|
+
**Исправление:**
|
|
40
|
+
- Explorer: если `base_url` относительный — поле остаётся пустым с placeholder `https://your-host/...`
|
|
41
|
+
- Explorer: если в окружении БД есть абсолютный `base_url` — подставляется автоматически
|
|
42
|
+
- `/api/try`: добавлена валидация — возвращает понятную ошибку, если URL не абсолютный
|
|
43
|
+
|
|
44
|
+
**Файлы:** `src/web/routes/explorer.ts`, `src/web/routes/api.ts`, `src/web/server.ts`
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 5. HTMX загружался с CDN (unpkg.com)
|
|
49
|
+
|
|
50
|
+
**Проблема:** HTMX подгружался с `https://unpkg.com/htmx.org@2.0.4`. В корпоративных сетях CDN может быть заблокирован → кнопка "Try it" и все HTMX-элементы не работают (JS не загрузился).
|
|
51
|
+
|
|
52
|
+
**Исправление:** HTMX скачан локально в `src/web/static/htmx.min.js` и сервируется с `/static/htmx.min.js`.
|
|
53
|
+
|
|
54
|
+
**Файлы:** `src/web/static/htmx.min.js`, `src/web/server.ts`, `src/web/views/layout.ts`
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 6. Self-signed сертификаты блокировали запросы
|
|
59
|
+
|
|
60
|
+
**Проблема:** Внутренние API используют self-signed сертификаты. `fetch()` в Bun по умолчанию отклоняет такие соединения с ошибкой `self signed certificate in certificate chain`.
|
|
61
|
+
|
|
62
|
+
**Исправление:** Добавлен `tls: { rejectUnauthorized: false }` во все `fetch()`:
|
|
63
|
+
- Explorer Try it (`/api/try`)
|
|
64
|
+
- Explorer authorize proxy (`/api/authorize`)
|
|
65
|
+
- Test runner (`http-client.ts`)
|
|
66
|
+
|
|
67
|
+
**Файлы:** `src/web/routes/api.ts`, `src/core/runner/http-client.ts`
|
package/install.ps1
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# apitool installer for Windows - downloads the latest release binary
|
|
2
|
+
# Usage: iwr https://raw.githubusercontent.com/kirrosh/apitool/master/install.ps1 | iex
|
|
3
|
+
|
|
4
|
+
$ErrorActionPreference = "Stop"
|
|
5
|
+
|
|
6
|
+
$REPO = "kirrosh/apitool"
|
|
7
|
+
$TARGET = "win-x64"
|
|
8
|
+
$ARTIFACT = "apitool-$TARGET.zip"
|
|
9
|
+
|
|
10
|
+
Write-Host "Detected platform: $TARGET" -ForegroundColor Cyan
|
|
11
|
+
|
|
12
|
+
# Get latest release tag
|
|
13
|
+
Write-Host "Fetching latest release..." -ForegroundColor Yellow
|
|
14
|
+
$RELEASE_URL = "https://api.github.com/repos/$REPO/releases/latest"
|
|
15
|
+
$TAG = (Invoke-RestMethod $RELEASE_URL).tag_name
|
|
16
|
+
|
|
17
|
+
if (-not $TAG) {
|
|
18
|
+
Write-Host "Error: Could not determine latest release tag" -ForegroundColor Red
|
|
19
|
+
exit 1
|
|
20
|
+
}
|
|
21
|
+
Write-Host "Latest release: $TAG" -ForegroundColor Green
|
|
22
|
+
|
|
23
|
+
# Download binary
|
|
24
|
+
$DOWNLOAD_URL = "https://github.com/$REPO/releases/download/$TAG/$ARTIFACT"
|
|
25
|
+
Write-Host "Downloading $DOWNLOAD_URL ..." -ForegroundColor Yellow
|
|
26
|
+
|
|
27
|
+
$TEMP_DIR = [System.IO.Path]::GetTempPath()
|
|
28
|
+
$DOWNLOAD_PATH = Join-Path $TEMP_DIR $ARTIFACT
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $DOWNLOAD_PATH -UseBasicParsing
|
|
32
|
+
} catch {
|
|
33
|
+
Write-Host "Error: Could not download release" -ForegroundColor Red
|
|
34
|
+
Write-Host $_.Exception.Message
|
|
35
|
+
exit 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Extract
|
|
39
|
+
$EXTRACT_DIR = Join-Path $TEMP_DIR "apitool-install"
|
|
40
|
+
if (Test-Path $EXTRACT_DIR) {
|
|
41
|
+
Remove-Item $EXTRACT_DIR -Recurse -Force
|
|
42
|
+
}
|
|
43
|
+
New-Item -ItemType Directory -Path $EXTRACT_DIR | Out-Null
|
|
44
|
+
|
|
45
|
+
Write-Host "Extracting..." -ForegroundColor Yellow
|
|
46
|
+
Expand-Archive -Path $DOWNLOAD_PATH -DestinationPath $EXTRACT_DIR -Force
|
|
47
|
+
|
|
48
|
+
$BINARY = Join-Path $EXTRACT_DIR "apitool.exe"
|
|
49
|
+
if (-not (Test-Path $BINARY)) {
|
|
50
|
+
Write-Host "Error: Binary not found in archive" -ForegroundColor Red
|
|
51
|
+
exit 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Install binary
|
|
55
|
+
$INSTALL_DIR = "$env:LOCALAPPDATA\apitool"
|
|
56
|
+
if (-not (Test-Path $INSTALL_DIR)) {
|
|
57
|
+
New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Copy-Item $BINARY $INSTALL_DIR -Force
|
|
61
|
+
$FINAL_PATH = Join-Path $INSTALL_DIR "apitool.exe"
|
|
62
|
+
|
|
63
|
+
Write-Host "Installed to $FINAL_PATH" -ForegroundColor Green
|
|
64
|
+
|
|
65
|
+
# Add to PATH if not already there
|
|
66
|
+
$PATH_ENTRY = $INSTALL_DIR
|
|
67
|
+
$currentPath = [System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
68
|
+
|
|
69
|
+
if ($currentPath -notlike "*$PATH_ENTRY*") {
|
|
70
|
+
[System.Environment]::SetEnvironmentVariable("Path", "$currentPath;$PATH_ENTRY", "User")
|
|
71
|
+
Write-Host "Added $PATH_ENTRY to User PATH" -ForegroundColor Green
|
|
72
|
+
Write-Host "Note: You may need to restart your terminal for changes to take effect" -ForegroundColor Yellow
|
|
73
|
+
} else {
|
|
74
|
+
Write-Host "$PATH_ENTRY already in PATH" -ForegroundColor Cyan
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Verify
|
|
78
|
+
& $FINAL_PATH --version
|
|
79
|
+
Write-Host "Done!" -ForegroundColor Green
|
|
80
|
+
Write-Host "Run 'apitool init' to set up a new project." -ForegroundColor Cyan
|
package/install.sh
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# apitool installer — downloads the latest release binary for your platform
|
|
3
|
+
# Usage: curl -fsSL https://raw.githubusercontent.com/kirrosh/apitool/master/install.sh | sh
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
REPO="kirrosh/apitool"
|
|
8
|
+
|
|
9
|
+
# Detect OS
|
|
10
|
+
OS=$(uname -s)
|
|
11
|
+
case "$OS" in
|
|
12
|
+
Linux) PLATFORM="linux" ;;
|
|
13
|
+
Darwin) PLATFORM="darwin" ;;
|
|
14
|
+
*)
|
|
15
|
+
echo "Error: Unsupported OS: $OS"
|
|
16
|
+
echo "For Windows, download the zip from https://github.com/$REPO/releases/latest"
|
|
17
|
+
exit 1
|
|
18
|
+
;;
|
|
19
|
+
esac
|
|
20
|
+
|
|
21
|
+
# Detect architecture
|
|
22
|
+
ARCH=$(uname -m)
|
|
23
|
+
case "$ARCH" in
|
|
24
|
+
x86_64|amd64) ARCH_SUFFIX="x64" ;;
|
|
25
|
+
arm64|aarch64) ARCH_SUFFIX="arm64" ;;
|
|
26
|
+
*)
|
|
27
|
+
echo "Error: Unsupported architecture: $ARCH"
|
|
28
|
+
exit 1
|
|
29
|
+
;;
|
|
30
|
+
esac
|
|
31
|
+
|
|
32
|
+
TARGET="${PLATFORM}-${ARCH_SUFFIX}"
|
|
33
|
+
echo "Detected platform: $TARGET"
|
|
34
|
+
|
|
35
|
+
# Get latest release tag
|
|
36
|
+
echo "Fetching latest release..."
|
|
37
|
+
RELEASE_URL="https://api.github.com/repos/$REPO/releases/latest"
|
|
38
|
+
TAG=$(curl -fsSL "$RELEASE_URL" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"//;s/".*//')
|
|
39
|
+
|
|
40
|
+
if [ -z "$TAG" ]; then
|
|
41
|
+
echo "Error: Could not determine latest release tag"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
echo "Latest release: $TAG"
|
|
45
|
+
|
|
46
|
+
# Download binary
|
|
47
|
+
ARTIFACT="apitool-${TARGET}.tar.gz"
|
|
48
|
+
DOWNLOAD_URL="https://github.com/$REPO/releases/download/$TAG/$ARTIFACT"
|
|
49
|
+
echo "Downloading $DOWNLOAD_URL ..."
|
|
50
|
+
|
|
51
|
+
TMPDIR=$(mktemp -d)
|
|
52
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
53
|
+
|
|
54
|
+
curl -fsSL "$DOWNLOAD_URL" -o "$TMPDIR/$ARTIFACT"
|
|
55
|
+
tar -xzf "$TMPDIR/$ARTIFACT" -C "$TMPDIR"
|
|
56
|
+
|
|
57
|
+
# Install binary
|
|
58
|
+
BINARY="$TMPDIR/apitool"
|
|
59
|
+
if [ ! -f "$BINARY" ]; then
|
|
60
|
+
echo "Error: Binary not found in archive"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
chmod +x "$BINARY"
|
|
64
|
+
|
|
65
|
+
# Choose install directory
|
|
66
|
+
INSTALL_DIR="/usr/local/bin"
|
|
67
|
+
if [ ! -w "$INSTALL_DIR" ]; then
|
|
68
|
+
# Try with sudo first
|
|
69
|
+
if command -v sudo >/dev/null 2>&1; then
|
|
70
|
+
echo "Need sudo to install to $INSTALL_DIR"
|
|
71
|
+
sudo cp "$BINARY" "$INSTALL_DIR/apitool"
|
|
72
|
+
sudo chmod +x "$INSTALL_DIR/apitool"
|
|
73
|
+
else
|
|
74
|
+
INSTALL_DIR="$HOME/.local/bin"
|
|
75
|
+
mkdir -p "$INSTALL_DIR"
|
|
76
|
+
cp "$BINARY" "$INSTALL_DIR/apitool"
|
|
77
|
+
echo "Installed to $INSTALL_DIR"
|
|
78
|
+
|
|
79
|
+
# Add to PATH in shell profile if not already there
|
|
80
|
+
case ":$PATH:" in
|
|
81
|
+
*":$INSTALL_DIR:"*) ;;
|
|
82
|
+
*)
|
|
83
|
+
PROFILE=""
|
|
84
|
+
if [ -f "$HOME/.zshrc" ]; then
|
|
85
|
+
PROFILE="$HOME/.zshrc"
|
|
86
|
+
elif [ -f "$HOME/.bashrc" ]; then
|
|
87
|
+
PROFILE="$HOME/.bashrc"
|
|
88
|
+
elif [ -f "$HOME/.profile" ]; then
|
|
89
|
+
PROFILE="$HOME/.profile"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if [ -n "$PROFILE" ]; then
|
|
93
|
+
echo "" >> "$PROFILE"
|
|
94
|
+
echo "# apitool" >> "$PROFILE"
|
|
95
|
+
echo "export PATH=\"\$HOME/.local/bin:\$PATH\"" >> "$PROFILE"
|
|
96
|
+
echo "Added $INSTALL_DIR to PATH in $PROFILE"
|
|
97
|
+
echo "Run: source $PROFILE (or open a new terminal)"
|
|
98
|
+
else
|
|
99
|
+
echo "Warning: $INSTALL_DIR is not in your PATH. Add it with:"
|
|
100
|
+
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
|
|
101
|
+
fi
|
|
102
|
+
;;
|
|
103
|
+
esac
|
|
104
|
+
fi
|
|
105
|
+
else
|
|
106
|
+
cp "$BINARY" "$INSTALL_DIR/apitool"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
echo "Installed to $INSTALL_DIR/apitool"
|
|
110
|
+
|
|
111
|
+
# Verify
|
|
112
|
+
"$INSTALL_DIR/apitool" --version
|
|
113
|
+
echo "Done! Run 'apitool init' to set up a new project."
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kirrosh/apitool",
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"description": "API testing platform — define tests in YAML, run from CLI or WebUI, generate from OpenAPI specs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"module": "index.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"api",
|
|
10
|
+
"testing",
|
|
11
|
+
"openapi",
|
|
12
|
+
"yaml",
|
|
13
|
+
"cli",
|
|
14
|
+
"rest",
|
|
15
|
+
"http"
|
|
16
|
+
],
|
|
17
|
+
"bin": {
|
|
18
|
+
"apitool": "src/cli/index.ts"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"apitool": "bun run src/cli/index.ts",
|
|
22
|
+
"test": "bun run test:unit && bun run test:mocked",
|
|
23
|
+
"test:unit": "bun test tests/db/ tests/parser/ tests/runner/ tests/generator/ tests/cli/args.test.ts tests/cli/chat.test.ts tests/cli/commands.test.ts tests/cli/doctor.test.ts tests/cli/envs.test.ts tests/cli/generate-env.test.ts tests/cli/init.test.ts tests/cli/runs.test.ts tests/cli/safe-run.test.ts tests/cli/update.test.ts tests/integration/ tests/web/",
|
|
24
|
+
"test:mocked": "bun run scripts/run-mocked-tests.ts",
|
|
25
|
+
"test:ai": "bun test tests/ai/",
|
|
26
|
+
"check": "tsc --noEmit --project tsconfig.json",
|
|
27
|
+
"build": "bun build --compile src/cli/index.ts --outfile apitool"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/bun": "latest"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"typescript": "^5"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@ai-sdk/anthropic": "^2",
|
|
37
|
+
"@ai-sdk/openai": "^2",
|
|
38
|
+
"@hono/zod-openapi": "^1.2.2",
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
40
|
+
"@readme/openapi-parser": "^5.5.0",
|
|
41
|
+
"ai": "^6",
|
|
42
|
+
"hono": "^4.12.2",
|
|
43
|
+
"openapi-types": "^12.1.3",
|
|
44
|
+
"zod": "^4.3.6"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs each mock.module() test file in a separate Bun subprocess
|
|
3
|
+
* to avoid Bun's module cache pollution bug (bun#7823, bun#12823).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const MOCKED_FILES = [
|
|
7
|
+
"tests/agent/tools/diagnose-failure.test.ts",
|
|
8
|
+
"tests/agent/tools/explore-api.test.ts",
|
|
9
|
+
"tests/agent/tools/generate-tests.test.ts",
|
|
10
|
+
"tests/agent/tools/manage-environment.test.ts",
|
|
11
|
+
"tests/agent/tools/query-results.test.ts",
|
|
12
|
+
"tests/agent/tools/run-tests.test.ts",
|
|
13
|
+
"tests/agent/tools/send-request.test.ts",
|
|
14
|
+
"tests/agent/tools/validate-tests.test.ts",
|
|
15
|
+
"tests/mcp/coverage-analysis.test.ts",
|
|
16
|
+
"tests/mcp/explore-api.test.ts",
|
|
17
|
+
"tests/mcp/send-request.test.ts",
|
|
18
|
+
"tests/cli/request.test.ts",
|
|
19
|
+
"tests/cli/coverage.test.ts",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const CONCURRENCY = 4;
|
|
23
|
+
let failed = 0;
|
|
24
|
+
|
|
25
|
+
async function runFile(file: string): Promise<boolean> {
|
|
26
|
+
const proc = Bun.spawn(["bun", "test", file], {
|
|
27
|
+
stdout: "inherit",
|
|
28
|
+
stderr: "inherit",
|
|
29
|
+
env: { ...process.env, FORCE_COLOR: "1" },
|
|
30
|
+
});
|
|
31
|
+
const code = await proc.exited;
|
|
32
|
+
return code === 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Run in batches of CONCURRENCY
|
|
36
|
+
for (let i = 0; i < MOCKED_FILES.length; i += CONCURRENCY) {
|
|
37
|
+
const batch = MOCKED_FILES.slice(i, i + CONCURRENCY);
|
|
38
|
+
const results = await Promise.all(batch.map(runFile));
|
|
39
|
+
failed += results.filter((ok) => !ok).length;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (failed > 0) {
|
|
43
|
+
console.error(`\n${failed} mocked test file(s) failed.`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
package/seed-demo.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getDb } from "./src/db/schema.ts";
|
|
2
|
+
import { createRun, finalizeRun, saveResults } from "./src/db/queries.ts";
|
|
3
|
+
import type { TestRunResult } from "./src/core/runner/types.ts";
|
|
4
|
+
|
|
5
|
+
getDb();
|
|
6
|
+
|
|
7
|
+
function makeRun(dayOffset: number, passRate: number): TestRunResult[] {
|
|
8
|
+
const d = new Date();
|
|
9
|
+
d.setDate(d.getDate() - dayOffset);
|
|
10
|
+
const total = 10;
|
|
11
|
+
const passed = Math.round(total * passRate);
|
|
12
|
+
const failed = total - passed;
|
|
13
|
+
|
|
14
|
+
const steps = [];
|
|
15
|
+
for (let i = 0; i < total; i++) {
|
|
16
|
+
const status = i < passed ? "pass" : "fail";
|
|
17
|
+
steps.push({
|
|
18
|
+
name: `Test step ${i + 1}`,
|
|
19
|
+
status: status as any,
|
|
20
|
+
duration_ms: 50 + Math.round(Math.random() * 500),
|
|
21
|
+
request: { method: "GET", url: `http://api.example.com/endpoint-${i}`, headers: {} },
|
|
22
|
+
response: { status: status === "pass" ? 200 : 500, headers: {}, body: "{}", duration_ms: 50 + Math.round(Math.random() * 500) },
|
|
23
|
+
assertions: [{ field: "status", rule: `equals ${status === "pass" ? 200 : 204}`, passed: status === "pass", actual: status === "pass" ? 200 : 500, expected: status === "pass" ? 200 : 204 }],
|
|
24
|
+
captures: {},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return [{
|
|
29
|
+
suite_name: dayOffset % 2 === 0 ? "Users API" : "Payments API",
|
|
30
|
+
started_at: d.toISOString(),
|
|
31
|
+
finished_at: new Date(d.getTime() + 1500).toISOString(),
|
|
32
|
+
total,
|
|
33
|
+
passed,
|
|
34
|
+
failed,
|
|
35
|
+
skipped: 0,
|
|
36
|
+
steps,
|
|
37
|
+
}];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Create 8 runs with varying pass rates
|
|
41
|
+
const rates = [1.0, 0.9, 0.8, 1.0, 0.7, 1.0, 0.9, 0.6];
|
|
42
|
+
for (let i = 0; i < rates.length; i++) {
|
|
43
|
+
const results = makeRun(rates.length - i, rates[i]!);
|
|
44
|
+
const runId = createRun({
|
|
45
|
+
started_at: results[0]!.started_at,
|
|
46
|
+
environment: i % 2 === 0 ? "staging" : "production",
|
|
47
|
+
trigger: "manual",
|
|
48
|
+
});
|
|
49
|
+
finalizeRun(runId, results);
|
|
50
|
+
saveResults(runId, results);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log("Seeded 8 demo runs into apitool.db");
|