Skip to content

Commit 0334f62

Browse files
committed
fix: update dependencies
Closes #152
1 parent f0c3c29 commit 0334f62

File tree

6 files changed

+205
-177
lines changed

6 files changed

+205
-177
lines changed

feature-runner/run-features.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,22 @@ const runner = await runFolder<World>({
2727
logObserver: {
2828
onDebug: (info, ...args) =>
2929
console.error(
30-
chalk.magenta(info.context.keyword),
30+
chalk.magenta(info.step.keyword),
3131
...args.map((arg) => chalk.cyan(print(arg))),
3232
),
3333
onError: (info, ...args) =>
3434
console.error(
35-
chalk.magenta(info.context.keyword),
35+
chalk.magenta(info.step.keyword),
3636
...args.map((arg) => chalk.red(print(arg))),
3737
),
3838
onInfo: (info, ...args) =>
3939
console.error(
40-
chalk.magenta(info.context.keyword),
40+
chalk.magenta(info.step.keyword),
4141
...args.map((arg) => chalk.green(print(arg))),
4242
),
4343
onProgress: (info, ...args) =>
4444
console.error(
45-
chalk.magenta(info.context.keyword),
45+
chalk.magenta(info.step.keyword),
4646
...args.map((arg) => chalk.yellow(print(arg))),
4747
),
4848
},

feature-runner/steps.ts

Lines changed: 122 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import {
88
} from '@aws-sdk/client-ssm'
99
import {
1010
codeBlockOrThrow,
11-
noMatch,
12-
type StepRunResult,
1311
type StepRunner,
14-
type StepRunnerArgs,
12+
regExpMatchedStep,
1513
} from '@nordicsemiconductor/bdd-markdown'
1614
import { randomUUID } from 'node:crypto'
1715
import { check, not, objectMatching, objectWithKeys } from 'tsmatchers'
1816
import { type World } from './run-features.js'
17+
import { Type } from '@sinclair/typebox'
18+
import pRetry from 'p-retry'
19+
1920
export const steps = ({
2021
ssm,
2122
}: {
@@ -24,118 +25,133 @@ export const steps = ({
2425
let Names: string[] = []
2526
return {
2627
steps: [
27-
async ({
28-
step,
29-
context,
30-
log: {
31-
step: { debug },
32-
},
33-
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
34-
const match = /^`(?<value>[^`]+)` is stored in `(?<name>[^`]+)`$/.exec(
35-
step.title,
36-
)
37-
if (match === null) return noMatch
38-
const Name = `/${context.stackName}/public/${match.groups?.name}`
39-
const value = match.groups?.value ?? ''
40-
debug(`${Name}: ${value}`)
41-
await ssm.send(
42-
new PutParameterCommand({
43-
Name,
44-
Value: `${value}`,
45-
Type: ParameterType.STRING,
46-
Overwrite: true,
28+
regExpMatchedStep(
29+
{
30+
regExp: /^`(?<value>[^`]+)` is stored in `(?<name>[^`]+)`$/,
31+
schema: Type.Object({
32+
value: Type.String({ minLength: 1 }),
33+
name: Type.String({ minLength: 1 }),
4734
}),
48-
)
49-
Names.push(Name)
50-
},
51-
async ({
52-
step,
53-
context,
54-
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
55-
const match =
56-
/^a random (?<type>string|number) is stored in `(?<storageName>[^`]+)`$/.exec(
57-
step.title,
35+
},
36+
async ({ context, log: { debug }, match: { name, value } }) => {
37+
const Name = `/${context.stackName}/public/${name}`
38+
debug(`${Name}: ${value}`)
39+
await ssm.send(
40+
new PutParameterCommand({
41+
Name,
42+
Value: `${value}`,
43+
Type: ParameterType.STRING,
44+
Overwrite: true,
45+
}),
5846
)
59-
if (match === null) return noMatch
60-
const value =
61-
match.groups?.type === 'string'
62-
? randomUUID()
63-
: Math.floor(Math.random() * 1000000)
64-
context[match.groups?.storageName ?? ''] = value
65-
},
66-
async ({
67-
step,
68-
context,
69-
log: {
70-
step: { debug },
47+
Names.push(Name)
48+
},
49+
),
50+
regExpMatchedStep(
51+
{
52+
regExp:
53+
/^a random (?<type>string|number) is stored in `(?<storageName>[^`]+)`$/,
54+
schema: Type.Object({
55+
type: Type.Union([Type.Literal('string'), Type.Literal('number')]),
56+
storageName: Type.String({ minLength: 1 }),
57+
}),
58+
},
59+
async ({ match: { type, storageName }, context }) => {
60+
const value =
61+
type === 'string'
62+
? randomUUID()
63+
: Math.floor(Math.random() * 1000000)
64+
context[storageName ?? ''] = value
7165
},
72-
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
73-
const match = /^`(?<name>[^`]+)` is deleted$/.exec(step.title)
74-
if (match === null) return noMatch
75-
const Name = `/${context.stackName}/public/${match.groups?.name}`
76-
debug(Name)
77-
await ssm.send(
78-
new DeleteParameterCommand({
79-
Name,
66+
),
67+
regExpMatchedStep(
68+
{
69+
regExp: /^`(?<name>[^`]+)` is deleted$/,
70+
schema: Type.Object({
71+
name: Type.String({ minLength: 1 }),
8072
}),
81-
)
82-
Names = Names.filter((n) => n !== Name)
83-
},
84-
async ({
85-
step,
86-
log: {
87-
step: { debug },
8873
},
89-
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
90-
const match =
91-
/^the result of GET `(?<url>[^`]+)` should match this JSON$/.exec(
92-
step.title,
74+
async ({ match: { name }, context, log: { debug } }) => {
75+
const Name = `/${context.stackName}/public/${name}`
76+
debug(Name)
77+
await ssm.send(
78+
new DeleteParameterCommand({
79+
Name,
80+
}),
9381
)
94-
if (match === null) return noMatch
95-
const res = await fetch(match?.groups?.url ?? '')
96-
res.headers.forEach((v, k) => debug(`${k}: ${v}`))
97-
const body = await res.text()
98-
debug(body)
99-
let registry: Record<string, any> = {}
100-
try {
101-
registry = JSON.parse(body)
102-
} catch {
103-
throw new Error(`Failed to parse body as JSON: ${body}`)
104-
}
105-
check(registry).is(
106-
objectMatching(JSON.parse(codeBlockOrThrow(step).code)),
107-
)
108-
return registry
109-
},
110-
async ({
111-
step,
112-
log: {
113-
step: { debug },
82+
Names = Names.filter((n) => n !== Name)
11483
},
115-
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
116-
const match =
117-
/^the S3 file `(?<s3Url>[^`]+)` should not have property `(?<property>[^`]+)`$/.exec(
118-
step.title,
84+
),
85+
regExpMatchedStep(
86+
{
87+
regExp: /^the result of GET `(?<url>[^`]+)` should match this JSON$/,
88+
schema: Type.Object({
89+
url: Type.String({ minLength: 1 }),
90+
}),
91+
},
92+
async ({ match: { url }, step, log: { debug } }) => {
93+
await pRetry(
94+
async () => {
95+
const res = await fetch(url ?? '')
96+
check(res.ok).is(true)
97+
res.headers.forEach((v, k) => debug(`${k}: ${v}`))
98+
const body = await res.text()
99+
debug(body)
100+
let result: Record<string, any> = {}
101+
try {
102+
result = JSON.parse(body)
103+
} catch {
104+
throw new Error(`Failed to parse body as JSON: ${body}`)
105+
}
106+
check(result).is(
107+
objectMatching(JSON.parse(codeBlockOrThrow(step).code)),
108+
)
109+
},
110+
{
111+
retries: 5,
112+
minTimeout: 5000,
113+
factor: 1.5,
114+
},
119115
)
120-
if (match === null) return noMatch
121-
const [bucket, file] = (match.groups?.s3Url ?? '').split('/')
122-
const res = await new S3Client({}).send(
123-
new GetObjectCommand({
124-
Bucket: bucket,
125-
Key: file,
116+
},
117+
),
118+
regExpMatchedStep(
119+
{
120+
regExp:
121+
/^the S3 file `(?<s3Url>[^`]+)` should not have property `(?<property>[^`]+)`$/,
122+
schema: Type.Object({
123+
s3Url: Type.String({ minLength: 1 }),
124+
property: Type.String({ minLength: 1 }),
126125
}),
127-
)
128-
const body = (await res.Body?.transformToString()) ?? ''
129-
debug(body)
130-
let registry: Record<string, any> = {}
131-
try {
132-
registry = JSON.parse(body)
133-
} catch {
134-
throw new Error(`Failed to parse body as JSON: ${body}`)
135-
}
136-
check(registry).is(not(objectWithKeys(match.groups?.property ?? '')))
137-
return registry
138-
},
126+
},
127+
async ({ match: { s3Url, property }, log: { debug } }) => {
128+
await pRetry(
129+
async () => {
130+
const [bucket, file] = (s3Url ?? '').split('/')
131+
const res = await new S3Client({}).send(
132+
new GetObjectCommand({
133+
Bucket: bucket,
134+
Key: file,
135+
}),
136+
)
137+
const body = (await res.Body?.transformToString()) ?? ''
138+
debug(body)
139+
let result: Record<string, any> = {}
140+
try {
141+
result = JSON.parse(body)
142+
} catch {
143+
throw new Error(`Failed to parse body as JSON: ${body}`)
144+
}
145+
check(result).is(not(objectWithKeys(property ?? '')))
146+
},
147+
{
148+
retries: 5,
149+
minTimeout: 5000,
150+
factor: 1.5,
151+
},
152+
)
153+
},
154+
),
139155
],
140156
cleanup: async () => {
141157
if (Names.length === 0) return

features/DeleteParameter.feature.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ retry:
33
initialDelay: 5000
44
needs:
55
- Get Parameters
6+
exampleContext:
7+
aRandomKey: 33ec3829-895f-4265-a11f-6c617a2e6b87
8+
bucketName: some-bucket
69
---
710

811
# Remove a deleted parameters

features/GetParameters.feature.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
---
22
retry:
33
initialDelay: 5000
4+
exampleContext:
5+
aRandomKey: 33ec3829-895f-4265-a11f-6c617a2e6b87
6+
randomNumber: 42
7+
randomString: some-value
8+
registryEndpoint: https://abc.cloudfront.net
49
---
510

611
# Get Parameters

0 commit comments

Comments
 (0)