From f3b9b9a8974d0d9be7fa587d6d060b48355178d3 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Mon, 31 Aug 2026 19:14:53 -0400 Subject: [PATCH 1/9] feat(instances): document calendar event linking --- openapi/components/paths/instances.yaml | 18 +++++ .../requests/UpdateInstanceRequest.yaml | 7 ++ test/arazzo.yaml | 81 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 openapi/components/requests/UpdateInstanceRequest.yaml diff --git a/openapi/components/paths/instances.yaml b/openapi/components/paths/instances.yaml index 29c6e772..d78fd690 100644 --- a/openapi/components/paths/instances.yaml +++ b/openapi/components/paths/instances.yaml @@ -114,6 +114,24 @@ paths: $ref: ../responses/instances/InstanceResponse.yaml "401": $ref: ../responses/MissingCredentialsError.yaml + put: + operationId: updateInstance + summary: Update Instance + description: Set or remove the calendar event linked to an instance. + tags: + - instances + requestBody: + content: + application/json: + schema: + $ref: ../requests/UpdateInstanceRequest.yaml + security: + - authCookie: [] + responses: + "200": + $ref: ../responses/instances/InstanceResponse.yaml + "400": + $ref: ../responses/RequestValidationError.yaml delete: operationId: closeInstance summary: Close Instance diff --git a/openapi/components/requests/UpdateInstanceRequest.yaml b/openapi/components/requests/UpdateInstanceRequest.yaml new file mode 100644 index 00000000..ac3a2b62 --- /dev/null +++ b/openapi/components/requests/UpdateInstanceRequest.yaml @@ -0,0 +1,7 @@ +title: UpdateInstanceRequest +type: object +properties: + calendarEntryId: + type: string + description: Calendar event to link to the instance. Send null to remove the current link. + nullable: true diff --git a/test/arazzo.yaml b/test/arazzo.yaml index dee0a142..4ca405f9 100644 --- a/test/arazzo.yaml +++ b/test/arazzo.yaml @@ -1342,6 +1342,71 @@ workflows: outputs: calendarId: $response.body#/id + - stepId: create-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: group + region: use + ownerId: $steps.create-group.outputs.groupId + groupAccessType: members + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + outputs: + instanceId: $response.body#/instanceId + + - stepId: link-instance-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == $steps.create-event.outputs.calendarId + + - stepId: unlink-instance-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == null + - stepId: calendar-events x-security: - schemeName: authCookie @@ -1517,6 +1582,22 @@ workflows: successCriteria: - condition: $statusCode == 404 + - stepId: close-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.closeInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + successCriteria: + - condition: $statusCode == 200 + - stepId: delete-group x-security: - schemeName: authCookie From 4a2db6ec9851d90cfaf2755dcfa218bc53979442 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 01:59:55 -0400 Subject: [PATCH 2/9] fix(instances): address calendar link review --- openapi/components/paths/instances.yaml | 12 +- .../requests/UpdateInstanceRequest.yaml | 2 + .../InstanceUpdateForbiddenError.yaml | 11 + test/arazzo.yaml | 497 +++++++++++++++--- 4 files changed, 456 insertions(+), 66 deletions(-) create mode 100644 openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml diff --git a/openapi/components/paths/instances.yaml b/openapi/components/paths/instances.yaml index d78fd690..644a5dee 100644 --- a/openapi/components/paths/instances.yaml +++ b/openapi/components/paths/instances.yaml @@ -117,10 +117,14 @@ paths: put: operationId: updateInstance summary: Update Instance - description: Set or remove the calendar event linked to an instance. + description: |- + Set or remove the calendar event linked to a group instance. + + The event must begin within the next six hours or have ended within the previous six hours. tags: - instances requestBody: + required: true content: application/json: schema: @@ -132,6 +136,12 @@ paths: $ref: ../responses/instances/InstanceResponse.yaml "400": $ref: ../responses/RequestValidationError.yaml + "401": + $ref: ../responses/MissingCredentialsError.yaml + "403": + $ref: ../responses/instances/InstanceUpdateForbiddenError.yaml + "404": + $ref: ../responses/ResourceNotFoundError.yaml delete: operationId: closeInstance summary: Close Instance diff --git a/openapi/components/requests/UpdateInstanceRequest.yaml b/openapi/components/requests/UpdateInstanceRequest.yaml index ac3a2b62..99e582ed 100644 --- a/openapi/components/requests/UpdateInstanceRequest.yaml +++ b/openapi/components/requests/UpdateInstanceRequest.yaml @@ -5,3 +5,5 @@ properties: type: string description: Calendar event to link to the instance. Send null to remove the current link. nullable: true +required: + - calendarEntryId diff --git a/openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml b/openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml new file mode 100644 index 00000000..65bd86d4 --- /dev/null +++ b/openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml @@ -0,0 +1,11 @@ +description: Error response due to not being allowed to update an instance +content: + application/json: + examples: + Not Allowed to Update Instance Example: + value: + error: + message: "You're not allowed to edit this instance․" + status_code: 403 + schema: + $ref: ../../schemas/Error.yaml diff --git a/test/arazzo.yaml b/test/arazzo.yaml index 4ca405f9..250e969a 100644 --- a/test/arazzo.yaml +++ b/test/arazzo.yaml @@ -1342,71 +1342,6 @@ workflows: outputs: calendarId: $response.body#/id - - stepId: create-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createInstance - requestBody: - contentType: application/json - payload: - worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - type: group - region: use - ownerId: $steps.create-group.outputs.groupId - groupAccessType: members - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - outputs: - instanceId: $response.body#/instanceId - - - stepId: link-instance-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: $steps.create-event.outputs.calendarId - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/calendarEntryId == $steps.create-event.outputs.calendarId - - - stepId: unlink-instance-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: null - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/calendarEntryId == null - - stepId: calendar-events x-security: - schemeName: authCookie @@ -1582,6 +1517,322 @@ workflows: successCriteria: - condition: $statusCode == 404 + - stepId: delete-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.deleteGroup + parameters: + - name: groupId + in: path + value: $steps.create-group.outputs.groupId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + - workflowId: update-instance-calendar-link-lifecycle + summary: Link and unlink a group instance to a calendar event, then remove both + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: starts-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+3600 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: ends-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+7200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: outside-window-starts-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+43200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: outside-window-ends-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+46800 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: short-code + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - openssl rand -hex 2 | tr -d '\r\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: create-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createGroup + requestBody: + contentType: application/json + payload: + name: Test Instance Calendar Link + privacy: private + joinState: invite + roleTemplate: default + shortCode: $steps.short-code.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + outputs: + groupId: $response.body#/id + + - stepId: create-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-group.outputs.groupId + requestBody: + contentType: application/json + payload: + accessType: group + category: other + description: Created by the test suite + title: Test Instance Calendar Link + sendCreationNotification: false + startsAt: $steps.starts-at.outputs.value + endsAt: $steps.ends-at.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-group + type: goto + stepId: delete-group + + outputs: + calendarId: $response.body#/id + + - stepId: create-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: group + region: use + ownerId: $steps.create-group.outputs.groupId + groupAccessType: members + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event + type: goto + stepId: delete-event + + outputs: + instanceId: $response.body#/instanceId + + - stepId: reject-malformed-calendar-entry + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: x + successCriteria: + - condition: $statusCode == 400 + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + + - stepId: link-instance-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == $steps.create-event.outputs.calendarId + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + + - stepId: unlink-instance-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == null + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + + - stepId: move-event-outside-window + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-group.outputs.groupId + - name: calendarId + in: path + value: $steps.create-event.outputs.calendarId + requestBody: + contentType: application/json + payload: + startsAt: $steps.outside-window-starts-at.outputs.value + endsAt: $steps.outside-window-ends-at.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + + - stepId: reject-event-outside-window + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 400 + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + - stepId: close-instance x-security: - schemeName: authCookie @@ -1597,6 +1848,30 @@ workflows: value: $steps.create-instance.outputs.instanceId successCriteria: - condition: $statusCode == 200 + onFailure: + - name: cleanup-event + type: goto + stepId: delete-event + + - stepId: delete-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.deleteGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-group.outputs.groupId + - name: calendarId + in: path + value: $steps.create-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-group + type: goto + stepId: delete-group - stepId: delete-group x-security: @@ -1614,6 +1889,98 @@ workflows: - name: stop type: end + - workflowId: update-instance-unauthenticated + summary: updateInstance refuses an anonymous caller + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: update-instance + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_00000000-0000-0000-0000-000000000000 + - name: instanceId + in: path + value: 0 + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 401 + + - workflowId: update-instance-forbidden + summary: updateInstance refuses an instance the caller cannot edit + parameters: + - reference: $components.parameters.userAgent + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + steps: + - stepId: update-instance + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_00000000-0000-0000-0000-000000000000 + - name: instanceId + in: path + value: 0 + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 403 + + - workflowId: update-instance-not-found + summary: updateInstance refuses an owned instance that does not exist + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: instance-id + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - printf '98765~private(%s)~region(use)~nonce(00000000-0000-0000-0000-000000000000)' "$1" + - sh + - $workflows.session.outputs.userId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: update-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.instance-id.outputs.value + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 404 + - workflowId: create-group-calendar-event-unauthenticated summary: createGroupCalendarEvent refuses an anonymous caller parameters: From 1147a46365b961a2fbd991436f4630b022ef5746 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 02:17:12 -0400 Subject: [PATCH 3/9] test(instances): isolate calendar link validation --- test/arazzo.yaml | 394 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 334 insertions(+), 60 deletions(-) diff --git a/test/arazzo.yaml b/test/arazzo.yaml index 250e969a..d3dbc883 100644 --- a/test/arazzo.yaml +++ b/test/arazzo.yaml @@ -1576,44 +1576,6 @@ workflows: outputs: value: $response.body#/stdout - - stepId: outside-window-starts-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+43200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: outside-window-ends-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+46800 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - stepId: short-code operationId: $sourceDescriptions.local.execute requestBody: @@ -1710,7 +1672,7 @@ workflows: outputs: instanceId: $response.body#/instanceId - - stepId: reject-malformed-calendar-entry + - stepId: link-instance-event x-security: - schemeName: authCookie values: @@ -1726,15 +1688,16 @@ workflows: requestBody: contentType: application/json payload: - calendarEntryId: x + calendarEntryId: $steps.create-event.outputs.calendarId successCriteria: - - condition: $statusCode == 400 + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == $steps.create-event.outputs.calendarId onFailure: - name: cleanup-instance type: goto stepId: close-instance - - stepId: link-instance-event + - stepId: unlink-instance-event x-security: - schemeName: authCookie values: @@ -1750,21 +1713,21 @@ workflows: requestBody: contentType: application/json payload: - calendarEntryId: $steps.create-event.outputs.calendarId + calendarEntryId: null successCriteria: - condition: $statusCode == 200 - - condition: $response.body#/calendarEntryId == $steps.create-event.outputs.calendarId + - condition: $response.body#/calendarEntryId == null onFailure: - name: cleanup-instance type: goto stepId: close-instance - - stepId: unlink-instance-event + - stepId: close-instance x-security: - schemeName: authCookie values: apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance + operationId: $sourceDescriptions.default.closeInstance parameters: - name: worldId in: path @@ -1772,24 +1735,19 @@ workflows: - name: instanceId in: path value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: null successCriteria: - condition: $statusCode == 200 - - condition: $response.body#/calendarEntryId == null onFailure: - - name: cleanup-instance + - name: cleanup-event type: goto - stepId: close-instance + stepId: delete-event - - stepId: move-event-outside-window + - stepId: delete-event x-security: - schemeName: authCookie values: apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateGroupCalendarEvent + operationId: $sourceDescriptions.default.deleteGroupCalendarEvent parameters: - name: groupId in: path @@ -1797,19 +1755,169 @@ workflows: - name: calendarId in: path value: $steps.create-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-group + type: goto + stepId: delete-group + + - stepId: delete-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.deleteGroup + parameters: + - name: groupId + in: path + value: $steps.create-group.outputs.groupId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + - workflowId: update-instance-calendar-link-outside-window + summary: Reject linking a group instance to an event outside the six-hour window + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: starts-at + operationId: $sourceDescriptions.local.execute requestBody: contentType: application/json payload: - startsAt: $steps.outside-window-starts-at.outputs.value - endsAt: $steps.outside-window-ends-at.outputs.value + command: sh + args: + - -c + - date -u -d '+43200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' successCriteria: - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 onFailure: - - name: cleanup-instance + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: ends-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+46800 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: short-code + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - openssl rand -hex 2 | tr -d '\r\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: create-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createGroup + requestBody: + contentType: application/json + payload: + name: Test Instance Calendar Window + privacy: private + joinState: invite + roleTemplate: default + shortCode: $steps.short-code.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + outputs: + groupId: $response.body#/id + + - stepId: create-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-group.outputs.groupId + requestBody: + contentType: application/json + payload: + accessType: group + category: other + description: Created by the test suite + title: Test Instance Calendar Window + sendCreationNotification: false + startsAt: $steps.starts-at.outputs.value + endsAt: $steps.ends-at.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-group type: goto - stepId: close-instance + stepId: delete-group + + outputs: + calendarId: $response.body#/id + + - stepId: create-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: group + region: use + ownerId: $steps.create-group.outputs.groupId + groupAccessType: members + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event + type: goto + stepId: delete-event + + outputs: + instanceId: $response.body#/instanceId - - stepId: reject-event-outside-window + - stepId: update-instance x-security: - schemeName: authCookie values: @@ -1889,6 +1997,172 @@ workflows: - name: stop type: end + - workflowId: update-instance-malformed-calendar-entry + summary: updateInstance rejects a malformed calendar entry ID + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: instance-id + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - printf '98765~private(%s)~region(use)~nonce(00000000-0000-0000-0000-000000000000)' "$1" + - sh + - $workflows.session.outputs.userId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: update-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.instance-id.outputs.value + requestBody: + contentType: application/json + payload: + calendarEntryId: x + successCriteria: + - condition: $statusCode == 400 + + - workflowId: update-instance-missing-calendar-entry + summary: updateInstance requires the calendar entry property + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: create-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: private + region: use + ownerId: $workflows.session.outputs.userId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + outputs: + instanceId: $response.body#/instanceId + + - stepId: update-instance + x-operation: + method: put + url: https://api.vrchat.cloud/api/1/instances/wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b:$steps.create-instance.outputs.instanceId + parameters: + - name: Cookie + in: header + value: auth=$workflows.session.outputs.sessionToken + requestBody: + contentType: application/json + payload: {} + successCriteria: + - condition: $statusCode == 500 + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + + - stepId: close-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.closeInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + successCriteria: + - condition: $statusCode == 200 + + - workflowId: update-instance-non-group + summary: updateInstance rejects a calendar entry on a non-group instance + parameters: + - reference: $components.parameters.userAgent + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + steps: + - stepId: create-instance + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: private + region: use + ownerId: $workflows.session.outputs.userId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + outputs: + instanceId: $response.body#/instanceId + + - stepId: update-instance + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 400 + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + + - stepId: close-instance + operationId: $sourceDescriptions.default.closeInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + successCriteria: + - condition: $statusCode == 200 + - workflowId: update-instance-unauthenticated summary: updateInstance refuses an anonymous caller parameters: From b883025d8e57a74915fee69c1aad9dddfa58d617 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 03:25:02 -0400 Subject: [PATCH 4/9] test(instances): cover cross-group calendar links --- test/arazzo.yaml | 282 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) diff --git a/test/arazzo.yaml b/test/arazzo.yaml index d3dbc883..de88bb50 100644 --- a/test/arazzo.yaml +++ b/test/arazzo.yaml @@ -1997,6 +1997,288 @@ workflows: - name: stop type: end + - workflowId: update-instance-calendar-link-cross-group + summary: Reject linking a group instance to an event owned by another group + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: starts-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+3600 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: ends-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+7200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: event-group-short-code + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - openssl rand -hex 2 | tr -d '\r\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: instance-group-short-code + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - openssl rand -hex 2 | tr -d '\r\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: create-event-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createGroup + requestBody: + contentType: application/json + payload: + name: Test Instance Calendar Event Owner + privacy: private + joinState: invite + roleTemplate: default + shortCode: $steps.event-group-short-code.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + outputs: + groupId: $response.body#/id + + - stepId: create-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-event-group.outputs.groupId + requestBody: + contentType: application/json + payload: + accessType: group + category: other + description: Created by the test suite + title: Test Cross-Group Instance Calendar Link + sendCreationNotification: false + startsAt: $steps.starts-at.outputs.value + endsAt: $steps.ends-at.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event-group + type: goto + stepId: delete-event-group + + outputs: + calendarId: $response.body#/id + + - stepId: create-instance-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createGroup + requestBody: + contentType: application/json + payload: + name: Test Instance Calendar Instance Owner + privacy: private + joinState: invite + roleTemplate: default + shortCode: $steps.instance-group-short-code.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event + type: goto + stepId: delete-event + + outputs: + groupId: $response.body#/id + + - stepId: create-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: group + region: use + ownerId: $steps.create-instance-group.outputs.groupId + groupAccessType: members + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-instance-group + type: goto + stepId: delete-instance-group + + outputs: + instanceId: $response.body#/instanceId + + - stepId: reject-cross-group-link + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 404 + - condition: $response.body#/error/message == 'Event not found․' + - condition: $response.body#/error/status_code == 404 + onFailure: + - name: cleanup-instance + type: goto + stepId: close-instance + + - stepId: close-instance + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.closeInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-instance.outputs.instanceId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == null + onFailure: + - name: cleanup-instance-group + type: goto + stepId: delete-instance-group + + - stepId: delete-instance-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.deleteGroup + parameters: + - name: groupId + in: path + value: $steps.create-instance-group.outputs.groupId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event + type: goto + stepId: delete-event + + - stepId: delete-event + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.deleteGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-event-group.outputs.groupId + - name: calendarId + in: path + value: $steps.create-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event-group + type: goto + stepId: delete-event-group + + - stepId: delete-event-group + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + operationId: $sourceDescriptions.default.deleteGroup + parameters: + - name: groupId + in: path + value: $steps.create-event-group.outputs.groupId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + - workflowId: update-instance-malformed-calendar-entry summary: updateInstance rejects a malformed calendar entry ID parameters: From 404c2b83a962f2b38cded542b53198f10b78fb9e Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 03:29:25 -0400 Subject: [PATCH 5/9] docs(instances): document cross-group calendar links --- openapi/components/paths/instances.yaml | 4 +++- .../instances/InstanceUpdateNotFoundError.yaml | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml diff --git a/openapi/components/paths/instances.yaml b/openapi/components/paths/instances.yaml index 644a5dee..f961194a 100644 --- a/openapi/components/paths/instances.yaml +++ b/openapi/components/paths/instances.yaml @@ -120,6 +120,8 @@ paths: description: |- Set or remove the calendar event linked to a group instance. + The event must belong to the group that owns the instance. + The event must begin within the next six hours or have ended within the previous six hours. tags: - instances @@ -141,7 +143,7 @@ paths: "403": $ref: ../responses/instances/InstanceUpdateForbiddenError.yaml "404": - $ref: ../responses/ResourceNotFoundError.yaml + $ref: ../responses/instances/InstanceUpdateNotFoundError.yaml delete: operationId: closeInstance summary: Close Instance diff --git a/openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml b/openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml new file mode 100644 index 00000000..0254e0b9 --- /dev/null +++ b/openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml @@ -0,0 +1,13 @@ +description: >- + Error response when the instance does not exist or the requested calendar + event belongs to another group +content: + application/json: + examples: + Event From Another Group Example: + value: + error: + message: Event not found․ + status_code: 404 + schema: + $ref: ../../schemas/Error.yaml From e2ec7415cfb3a00ea4e39de245ebfc627bd36143 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 04:21:43 -0400 Subject: [PATCH 6/9] docs(instances): refine update error responses --- openapi/components/paths/instances.yaml | 2 +- .../InstanceUpdateNotFoundError.yaml | 5 ++ .../InstanceUpdateValidationError.yaml | 16 +++++ test/arazzo.yaml | 63 +------------------ 4 files changed, 23 insertions(+), 63 deletions(-) create mode 100644 openapi/components/responses/instances/InstanceUpdateValidationError.yaml diff --git a/openapi/components/paths/instances.yaml b/openapi/components/paths/instances.yaml index f961194a..edfd0681 100644 --- a/openapi/components/paths/instances.yaml +++ b/openapi/components/paths/instances.yaml @@ -137,7 +137,7 @@ paths: "200": $ref: ../responses/instances/InstanceResponse.yaml "400": - $ref: ../responses/RequestValidationError.yaml + $ref: ../responses/instances/InstanceUpdateValidationError.yaml "401": $ref: ../responses/MissingCredentialsError.yaml "403": diff --git a/openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml b/openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml index 0254e0b9..6f810a51 100644 --- a/openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml +++ b/openapi/components/responses/instances/InstanceUpdateNotFoundError.yaml @@ -9,5 +9,10 @@ content: error: message: Event not found․ status_code: 404 + Instance Not Found Example: + value: + error: + message: Instance not found․ + status_code: 404 schema: $ref: ../../schemas/Error.yaml diff --git a/openapi/components/responses/instances/InstanceUpdateValidationError.yaml b/openapi/components/responses/instances/InstanceUpdateValidationError.yaml new file mode 100644 index 00000000..c2c127ce --- /dev/null +++ b/openapi/components/responses/instances/InstanceUpdateValidationError.yaml @@ -0,0 +1,16 @@ +description: Error response when an instance's calendar event link cannot be updated +content: + application/json: + examples: + Event Outside Link Window Example: + value: + error: + message: You can only link instances to events starting within 6 hours‚ or that have ended within the last 6 hours․ + status_code: 400 + Non-Group Instance Example: + value: + error: + message: "'calendarEntryId' is only allowed for group instances․" + status_code: 400 + schema: + $ref: ../../schemas/Error.yaml diff --git a/test/arazzo.yaml b/test/arazzo.yaml index de88bb50..44fa34fd 100644 --- a/test/arazzo.yaml +++ b/test/arazzo.yaml @@ -2325,69 +2325,8 @@ workflows: successCriteria: - condition: $statusCode == 400 - - workflowId: update-instance-missing-calendar-entry - summary: updateInstance requires the calendar entry property - parameters: - - reference: $components.parameters.userAgent - steps: - - stepId: create-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createInstance - requestBody: - contentType: application/json - payload: - worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - type: private - region: use - ownerId: $workflows.session.outputs.userId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - outputs: - instanceId: $response.body#/instanceId - - - stepId: update-instance - x-operation: - method: put - url: https://api.vrchat.cloud/api/1/instances/wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b:$steps.create-instance.outputs.instanceId - parameters: - - name: Cookie - in: header - value: auth=$workflows.session.outputs.sessionToken - requestBody: - contentType: application/json - payload: {} - successCriteria: - - condition: $statusCode == 500 - onFailure: - - name: cleanup-instance - type: goto - stepId: close-instance - - - stepId: close-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.closeInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - successCriteria: - - condition: $statusCode == 200 - - workflowId: update-instance-non-group - summary: updateInstance rejects a calendar entry on a non-group instance + summary: updateInstance rejects calendar link updates on a non-group instance parameters: - reference: $components.parameters.userAgent x-security: From b25f54b6824dcb46b971e0a04d30484324ae57c8 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 13:53:04 -0400 Subject: [PATCH 7/9] test(instances): remove redundant update workflows --- test/arazzo.yaml | 943 ----------------------------------------------- 1 file changed, 943 deletions(-) diff --git a/test/arazzo.yaml b/test/arazzo.yaml index 44fa34fd..dee0a142 100644 --- a/test/arazzo.yaml +++ b/test/arazzo.yaml @@ -1533,949 +1533,6 @@ workflows: - name: stop type: end - - workflowId: update-instance-calendar-link-lifecycle - summary: Link and unlink a group instance to a calendar event, then remove both - parameters: - - reference: $components.parameters.userAgent - steps: - - stepId: starts-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+3600 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: ends-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+7200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: short-code - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - openssl rand -hex 2 | tr -d '\r\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: create-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createGroup - requestBody: - contentType: application/json - payload: - name: Test Instance Calendar Link - privacy: private - joinState: invite - roleTemplate: default - shortCode: $steps.short-code.outputs.value - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - outputs: - groupId: $response.body#/id - - - stepId: create-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createGroupCalendarEvent - parameters: - - name: groupId - in: path - value: $steps.create-group.outputs.groupId - requestBody: - contentType: application/json - payload: - accessType: group - category: other - description: Created by the test suite - title: Test Instance Calendar Link - sendCreationNotification: false - startsAt: $steps.starts-at.outputs.value - endsAt: $steps.ends-at.outputs.value - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-group - type: goto - stepId: delete-group - - outputs: - calendarId: $response.body#/id - - - stepId: create-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createInstance - requestBody: - contentType: application/json - payload: - worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - type: group - region: use - ownerId: $steps.create-group.outputs.groupId - groupAccessType: members - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event - type: goto - stepId: delete-event - - outputs: - instanceId: $response.body#/instanceId - - - stepId: link-instance-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: $steps.create-event.outputs.calendarId - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/calendarEntryId == $steps.create-event.outputs.calendarId - onFailure: - - name: cleanup-instance - type: goto - stepId: close-instance - - - stepId: unlink-instance-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: null - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/calendarEntryId == null - onFailure: - - name: cleanup-instance - type: goto - stepId: close-instance - - - stepId: close-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.closeInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event - type: goto - stepId: delete-event - - - stepId: delete-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.deleteGroupCalendarEvent - parameters: - - name: groupId - in: path - value: $steps.create-group.outputs.groupId - - name: calendarId - in: path - value: $steps.create-event.outputs.calendarId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-group - type: goto - stepId: delete-group - - - stepId: delete-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.deleteGroup - parameters: - - name: groupId - in: path - value: $steps.create-group.outputs.groupId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - - workflowId: update-instance-calendar-link-outside-window - summary: Reject linking a group instance to an event outside the six-hour window - parameters: - - reference: $components.parameters.userAgent - steps: - - stepId: starts-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+43200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: ends-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+46800 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: short-code - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - openssl rand -hex 2 | tr -d '\r\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: create-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createGroup - requestBody: - contentType: application/json - payload: - name: Test Instance Calendar Window - privacy: private - joinState: invite - roleTemplate: default - shortCode: $steps.short-code.outputs.value - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - outputs: - groupId: $response.body#/id - - - stepId: create-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createGroupCalendarEvent - parameters: - - name: groupId - in: path - value: $steps.create-group.outputs.groupId - requestBody: - contentType: application/json - payload: - accessType: group - category: other - description: Created by the test suite - title: Test Instance Calendar Window - sendCreationNotification: false - startsAt: $steps.starts-at.outputs.value - endsAt: $steps.ends-at.outputs.value - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-group - type: goto - stepId: delete-group - - outputs: - calendarId: $response.body#/id - - - stepId: create-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createInstance - requestBody: - contentType: application/json - payload: - worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - type: group - region: use - ownerId: $steps.create-group.outputs.groupId - groupAccessType: members - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event - type: goto - stepId: delete-event - - outputs: - instanceId: $response.body#/instanceId - - - stepId: update-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: $steps.create-event.outputs.calendarId - successCriteria: - - condition: $statusCode == 400 - onFailure: - - name: cleanup-instance - type: goto - stepId: close-instance - - - stepId: close-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.closeInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event - type: goto - stepId: delete-event - - - stepId: delete-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.deleteGroupCalendarEvent - parameters: - - name: groupId - in: path - value: $steps.create-group.outputs.groupId - - name: calendarId - in: path - value: $steps.create-event.outputs.calendarId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-group - type: goto - stepId: delete-group - - - stepId: delete-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.deleteGroup - parameters: - - name: groupId - in: path - value: $steps.create-group.outputs.groupId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - - workflowId: update-instance-calendar-link-cross-group - summary: Reject linking a group instance to an event owned by another group - parameters: - - reference: $components.parameters.userAgent - steps: - - stepId: starts-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+3600 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: ends-at - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - date -u -d '+7200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: event-group-short-code - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - openssl rand -hex 2 | tr -d '\r\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: instance-group-short-code - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - openssl rand -hex 2 | tr -d '\r\n' - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: create-event-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createGroup - requestBody: - contentType: application/json - payload: - name: Test Instance Calendar Event Owner - privacy: private - joinState: invite - roleTemplate: default - shortCode: $steps.event-group-short-code.outputs.value - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - outputs: - groupId: $response.body#/id - - - stepId: create-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createGroupCalendarEvent - parameters: - - name: groupId - in: path - value: $steps.create-event-group.outputs.groupId - requestBody: - contentType: application/json - payload: - accessType: group - category: other - description: Created by the test suite - title: Test Cross-Group Instance Calendar Link - sendCreationNotification: false - startsAt: $steps.starts-at.outputs.value - endsAt: $steps.ends-at.outputs.value - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event-group - type: goto - stepId: delete-event-group - - outputs: - calendarId: $response.body#/id - - - stepId: create-instance-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createGroup - requestBody: - contentType: application/json - payload: - name: Test Instance Calendar Instance Owner - privacy: private - joinState: invite - roleTemplate: default - shortCode: $steps.instance-group-short-code.outputs.value - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event - type: goto - stepId: delete-event - - outputs: - groupId: $response.body#/id - - - stepId: create-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.createInstance - requestBody: - contentType: application/json - payload: - worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - type: group - region: use - ownerId: $steps.create-instance-group.outputs.groupId - groupAccessType: members - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-instance-group - type: goto - stepId: delete-instance-group - - outputs: - instanceId: $response.body#/instanceId - - - stepId: reject-cross-group-link - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: $steps.create-event.outputs.calendarId - successCriteria: - - condition: $statusCode == 404 - - condition: $response.body#/error/message == 'Event not found․' - - condition: $response.body#/error/status_code == 404 - onFailure: - - name: cleanup-instance - type: goto - stepId: close-instance - - - stepId: close-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.closeInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/calendarEntryId == null - onFailure: - - name: cleanup-instance-group - type: goto - stepId: delete-instance-group - - - stepId: delete-instance-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.deleteGroup - parameters: - - name: groupId - in: path - value: $steps.create-instance-group.outputs.groupId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event - type: goto - stepId: delete-event - - - stepId: delete-event - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.deleteGroupCalendarEvent - parameters: - - name: groupId - in: path - value: $steps.create-event-group.outputs.groupId - - name: calendarId - in: path - value: $steps.create-event.outputs.calendarId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: cleanup-event-group - type: goto - stepId: delete-event-group - - - stepId: delete-event-group - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.deleteGroup - parameters: - - name: groupId - in: path - value: $steps.create-event-group.outputs.groupId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - - workflowId: update-instance-malformed-calendar-entry - summary: updateInstance rejects a malformed calendar entry ID - parameters: - - reference: $components.parameters.userAgent - steps: - - stepId: instance-id - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - printf '98765~private(%s)~region(use)~nonce(00000000-0000-0000-0000-000000000000)' "$1" - - sh - - $workflows.session.outputs.userId - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: update-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.instance-id.outputs.value - requestBody: - contentType: application/json - payload: - calendarEntryId: x - successCriteria: - - condition: $statusCode == 400 - - - workflowId: update-instance-non-group - summary: updateInstance rejects calendar link updates on a non-group instance - parameters: - - reference: $components.parameters.userAgent - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - steps: - - stepId: create-instance - operationId: $sourceDescriptions.default.createInstance - requestBody: - contentType: application/json - payload: - worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - type: private - region: use - ownerId: $workflows.session.outputs.userId - successCriteria: - - condition: $statusCode == 200 - onFailure: - - name: stop - type: end - - outputs: - instanceId: $response.body#/instanceId - - - stepId: update-instance - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - requestBody: - contentType: application/json - payload: - calendarEntryId: null - successCriteria: - - condition: $statusCode == 400 - onFailure: - - name: cleanup-instance - type: goto - stepId: close-instance - - - stepId: close-instance - operationId: $sourceDescriptions.default.closeInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.create-instance.outputs.instanceId - successCriteria: - - condition: $statusCode == 200 - - - workflowId: update-instance-unauthenticated - summary: updateInstance refuses an anonymous caller - parameters: - - reference: $components.parameters.userAgent - steps: - - stepId: update-instance - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_00000000-0000-0000-0000-000000000000 - - name: instanceId - in: path - value: 0 - requestBody: - contentType: application/json - payload: - calendarEntryId: null - successCriteria: - - condition: $statusCode == 401 - - - workflowId: update-instance-forbidden - summary: updateInstance refuses an instance the caller cannot edit - parameters: - - reference: $components.parameters.userAgent - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - steps: - - stepId: update-instance - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_00000000-0000-0000-0000-000000000000 - - name: instanceId - in: path - value: 0 - requestBody: - contentType: application/json - payload: - calendarEntryId: null - successCriteria: - - condition: $statusCode == 403 - - - workflowId: update-instance-not-found - summary: updateInstance refuses an owned instance that does not exist - parameters: - - reference: $components.parameters.userAgent - steps: - - stepId: instance-id - operationId: $sourceDescriptions.local.execute - requestBody: - contentType: application/json - payload: - command: sh - args: - - -c - - printf '98765~private(%s)~region(use)~nonce(00000000-0000-0000-0000-000000000000)' "$1" - - sh - - $workflows.session.outputs.userId - successCriteria: - - condition: $statusCode == 200 - - condition: $response.body#/exitCode == 0 - onFailure: - - name: stop - type: end - - outputs: - value: $response.body#/stdout - - - stepId: update-instance - x-security: - - schemeName: authCookie - values: - apiKey: $workflows.session.outputs.sessionToken - operationId: $sourceDescriptions.default.updateInstance - parameters: - - name: worldId - in: path - value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b - - name: instanceId - in: path - value: $steps.instance-id.outputs.value - requestBody: - contentType: application/json - payload: - calendarEntryId: null - successCriteria: - - condition: $statusCode == 404 - - workflowId: create-group-calendar-event-unauthenticated summary: createGroupCalendarEvent refuses an anonymous caller parameters: From 41c01535dd22996055c6d5ccb64f6117bbc84214 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 13:53:51 -0400 Subject: [PATCH 8/9] test(instances): consolidate calendar link coverage --- test/arazzo.yaml | 541 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 541 insertions(+) diff --git a/test/arazzo.yaml b/test/arazzo.yaml index dee0a142..06c4eeea 100644 --- a/test/arazzo.yaml +++ b/test/arazzo.yaml @@ -6701,6 +6701,547 @@ workflows: successCriteria: - condition: $statusCode == 401 + - workflowId: update-instance-calendar-link-lifecycle + summary: Validate group instance calendar links and clean up every fixture + parameters: + - reference: $components.parameters.userAgent + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + steps: + - stepId: in-window-starts-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+3600 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: in-window-ends-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+7200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: outside-window-starts-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+43200 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: outside-window-ends-at + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - date -u -d '+46800 seconds' +%Y-%m-%dT%H:%M:%S.%3NZ | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: instance-group-short-code + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - openssl rand -hex 2 | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: event-group-short-code + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - openssl rand -hex 2 | tr -d '\n' + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: create-instance-group + operationId: $sourceDescriptions.default.createGroup + requestBody: + contentType: application/json + payload: + name: Test Instance Calendar Links + privacy: private + joinState: invite + roleTemplate: default + shortCode: $steps.instance-group-short-code.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + outputs: + groupId: $response.body#/id + + - stepId: create-linkable-event + operationId: $sourceDescriptions.default.createGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-instance-group.outputs.groupId + requestBody: + contentType: application/json + payload: + accessType: group + category: other + description: Created by the test suite + title: Test Instance Calendar Link + sendCreationNotification: false + startsAt: $steps.in-window-starts-at.outputs.value + endsAt: $steps.in-window-ends-at.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-instance-group + type: goto + stepId: delete-instance-group + + outputs: + calendarId: $response.body#/id + + - stepId: create-outside-window-event + operationId: $sourceDescriptions.default.createGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-instance-group.outputs.groupId + requestBody: + contentType: application/json + payload: + accessType: group + category: other + description: Created by the test suite + title: Test Instance Calendar Window + sendCreationNotification: false + startsAt: $steps.outside-window-starts-at.outputs.value + endsAt: $steps.outside-window-ends-at.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-instance-group + type: goto + stepId: delete-instance-group + + outputs: + calendarId: $response.body#/id + + - stepId: create-event-group + operationId: $sourceDescriptions.default.createGroup + requestBody: + contentType: application/json + payload: + name: Test Instance Calendar Event Owner + privacy: private + joinState: invite + roleTemplate: default + shortCode: $steps.event-group-short-code.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-instance-group + type: goto + stepId: delete-instance-group + + outputs: + groupId: $response.body#/id + + - stepId: create-cross-group-event + operationId: $sourceDescriptions.default.createGroupCalendarEvent + parameters: + - name: groupId + in: path + value: $steps.create-event-group.outputs.groupId + requestBody: + contentType: application/json + payload: + accessType: group + category: other + description: Created by the test suite + title: Test Cross-Group Instance Calendar Link + sendCreationNotification: false + startsAt: $steps.in-window-starts-at.outputs.value + endsAt: $steps.in-window-ends-at.outputs.value + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event-group + type: goto + stepId: delete-event-group + + outputs: + calendarId: $response.body#/id + + - stepId: create-group-instance + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: group + region: use + ownerId: $steps.create-instance-group.outputs.groupId + groupAccessType: members + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-event-group + type: goto + stepId: delete-event-group + + outputs: + instanceId: $response.body#/instanceId + + - stepId: create-private-instance + operationId: $sourceDescriptions.default.createInstance + requestBody: + contentType: application/json + payload: + worldId: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + type: private + region: use + ownerId: $workflows.session.outputs.userId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-group-instance + type: goto + stepId: close-group-instance + + outputs: + instanceId: $response.body#/instanceId + + - stepId: link-instance-event + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-group-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-linkable-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == $steps.create-linkable-event.outputs.calendarId + onFailure: + - name: cleanup-private-instance + type: goto + stepId: close-private-instance + + - stepId: unlink-instance-event + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-group-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == null + onFailure: + - name: cleanup-private-instance + type: goto + stepId: close-private-instance + + - stepId: reject-outside-window-link + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-group-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-outside-window-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 400 + - condition: $response.body#/error/message == 'You can only link instances to events starting within 6 hours‚ or that have ended within the last 6 hours․' + - condition: $response.body#/error/status_code == 400 + onFailure: + - name: cleanup-private-instance + type: goto + stepId: close-private-instance + + - stepId: reject-cross-group-link + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-group-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-cross-group-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 404 + - condition: $response.body#/error/message == 'Event not found․' + - condition: $response.body#/error/status_code == 404 + onFailure: + - name: cleanup-private-instance + type: goto + stepId: close-private-instance + + - stepId: reject-non-group-link + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-private-instance.outputs.instanceId + requestBody: + contentType: application/json + payload: + calendarEntryId: $steps.create-linkable-event.outputs.calendarId + successCriteria: + - condition: $statusCode == 400 + - condition: $response.body#/error/message == "'calendarEntryId' is only allowed for group instances․" + - condition: $response.body#/error/status_code == 400 + onFailure: + - name: cleanup-private-instance + type: goto + stepId: close-private-instance + + - stepId: close-private-instance + operationId: $sourceDescriptions.default.closeInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-private-instance.outputs.instanceId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-group-instance + type: goto + stepId: close-group-instance + + - stepId: close-group-instance + operationId: $sourceDescriptions.default.closeInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.create-group-instance.outputs.instanceId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/calendarEntryId == null + onFailure: + - name: cleanup-event-group + type: goto + stepId: delete-event-group + + - stepId: delete-event-group + operationId: $sourceDescriptions.default.deleteGroup + parameters: + - name: groupId + in: path + value: $steps.create-event-group.outputs.groupId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: cleanup-instance-group + type: goto + stepId: delete-instance-group + + - stepId: delete-instance-group + operationId: $sourceDescriptions.default.deleteGroup + parameters: + - name: groupId + in: path + value: $steps.create-instance-group.outputs.groupId + successCriteria: + - condition: $statusCode == 200 + onFailure: + - name: stop + type: end + + - workflowId: update-instance-unauthenticated + summary: updateInstance refuses an anonymous caller + parameters: + - reference: $components.parameters.userAgent + steps: + - stepId: update-instance + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_00000000-0000-0000-0000-000000000000 + - name: instanceId + in: path + value: 0 + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 401 + + - workflowId: update-instance-forbidden + summary: updateInstance refuses an instance the caller cannot edit + parameters: + - reference: $components.parameters.userAgent + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + steps: + - stepId: update-instance + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_00000000-0000-0000-0000-000000000000 + - name: instanceId + in: path + value: 0 + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 403 + - condition: $response.body#/error/message == "You're not allowed to edit this instance․" + - condition: $response.body#/error/status_code == 403 + + - workflowId: update-instance-not-found + summary: updateInstance refuses an owned instance that does not exist + parameters: + - reference: $components.parameters.userAgent + x-security: + - schemeName: authCookie + values: + apiKey: $workflows.session.outputs.sessionToken + steps: + - stepId: instance-id + operationId: $sourceDescriptions.local.execute + requestBody: + contentType: application/json + payload: + command: sh + args: + - -c + - printf '98765~private(%s)~region(use)~nonce(00000000-0000-0000-0000-000000000000)' "$1" + - sh + - $workflows.session.outputs.userId + successCriteria: + - condition: $statusCode == 200 + - condition: $response.body#/exitCode == 0 + onFailure: + - name: stop + type: end + + outputs: + value: $response.body#/stdout + + - stepId: update-instance + operationId: $sourceDescriptions.default.updateInstance + parameters: + - name: worldId + in: path + value: wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b + - name: instanceId + in: path + value: $steps.instance-id.outputs.value + requestBody: + contentType: application/json + payload: + calendarEntryId: null + successCriteria: + - condition: $statusCode == 404 + - condition: $response.body#/error/message == 'Instance not found․' + - condition: $response.body#/error/status_code == 404 + - workflowId: close-instance-not-found summary: closeInstance refuses a resource that does not exist parameters: From 9565a5a6bc59f2a7b51462d04452230fedccce26 Mon Sep 17 00:00:00 2001 From: BASICBIT Date: Thu, 3 Sep 2026 13:56:25 -0400 Subject: [PATCH 9/9] docs(instances): document calendar link permissions --- openapi/components/paths/instances.yaml | 2 +- .../instances/InstanceUpdateForbiddenError.yaml | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/openapi/components/paths/instances.yaml b/openapi/components/paths/instances.yaml index edfd0681..f6aa2860 100644 --- a/openapi/components/paths/instances.yaml +++ b/openapi/components/paths/instances.yaml @@ -120,7 +120,7 @@ paths: description: |- Set or remove the calendar event linked to a group instance. - The event must belong to the group that owns the instance. + Updating a group instance requires both the `group-instance-manage` and `group-instance-calendar-link` permissions. The event must begin within the next six hours or have ended within the previous six hours. tags: diff --git a/openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml b/openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml index 65bd86d4..3524eae2 100644 --- a/openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml +++ b/openapi/components/responses/instances/InstanceUpdateForbiddenError.yaml @@ -1,8 +1,13 @@ -description: Error response due to not being allowed to update an instance +description: Error response when the caller lacks permission to manage the instance or its calendar link content: application/json: examples: - Not Allowed to Update Instance Example: + Missing Calendar Link Permission Example: + value: + error: + message: You're not allowed to create or change an instance linked to an event․ + status_code: 403 + Missing Instance Management Permission Example: value: error: message: "You're not allowed to edit this instance․"