@stigmer/sdk 3.35.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/activity.d.ts.map +1 -1
- package/activity.js.map +1 -1
- package/artifact-routing.d.ts.map +1 -1
- package/artifact-routing.js.map +1 -1
- package/authorization-config.d.ts.map +1 -1
- package/authorization-config.js.map +1 -1
- package/billing.d.ts.map +1 -1
- package/billing.js.map +1 -1
- package/config.d.ts.map +1 -1
- package/config.js.map +1 -1
- package/cursor-accounts.d.ts.map +1 -1
- package/cursor-accounts.js.map +1 -1
- package/ensure-identity-account.d.ts.map +1 -1
- package/ensure-identity-account.js.map +1 -1
- package/errors.d.ts.map +1 -1
- package/errors.js.map +1 -1
- package/execution/approval-provenance.d.ts.map +1 -1
- package/execution/approval-provenance.js.map +1 -1
- package/execution/conversation-rules.d.ts.map +1 -1
- package/execution/conversation-rules.js.map +1 -1
- package/execution/execution-phases.d.ts.map +1 -1
- package/execution/execution-phases.js.map +1 -1
- package/execution/file-review-fold.d.ts.map +1 -1
- package/execution/file-review-fold.js.map +1 -1
- package/execution/to-display-file-change.d.ts.map +1 -1
- package/execution/to-display-file-change.js.map +1 -1
- package/execution/tool-view.d.ts.map +1 -1
- package/execution/tool-view.js.map +1 -1
- package/execution/transcript.d.ts.map +1 -1
- package/execution/transcript.js.map +1 -1
- package/gen/agent.d.ts.map +1 -1
- package/gen/agent.js.map +1 -1
- package/gen/agentchannel.d.ts.map +1 -1
- package/gen/agentchannel.js.map +1 -1
- package/gen/agentexecution.d.ts.map +1 -1
- package/gen/agentexecution.js.map +1 -1
- package/gen/agentinstance.d.ts.map +1 -1
- package/gen/agentinstance.js.map +1 -1
- package/gen/agentshare.d.ts.map +1 -1
- package/gen/agentshare.js.map +1 -1
- package/gen/apikey.d.ts.map +1 -1
- package/gen/apikey.js.map +1 -1
- package/gen/artifact.d.ts.map +1 -1
- package/gen/artifact.js.map +1 -1
- package/gen/authorization-config.d.ts.map +1 -1
- package/gen/authorization-config.js +2 -2
- package/gen/authorization-config.js.map +1 -1
- package/gen/bidi-stream.d.ts.map +1 -1
- package/gen/bidi-stream.js.map +1 -1
- package/gen/channelapp.d.ts.map +1 -1
- package/gen/channelapp.js.map +1 -1
- package/gen/client.d.ts.map +1 -1
- package/gen/client.js.map +1 -1
- package/gen/environment.d.ts.map +1 -1
- package/gen/environment.js.map +1 -1
- package/gen/errors.d.ts.map +1 -1
- package/gen/errors.js.map +1 -1
- package/gen/executioncontext.d.ts.map +1 -1
- package/gen/executioncontext.js.map +1 -1
- package/gen/iampolicy.d.ts.map +1 -1
- package/gen/iampolicy.js.map +1 -1
- package/gen/identityaccount.d.ts.map +1 -1
- package/gen/identityaccount.js.map +1 -1
- package/gen/identityprovider.d.ts.map +1 -1
- package/gen/identityprovider.js.map +1 -1
- package/gen/invitation.d.ts.map +1 -1
- package/gen/invitation.js.map +1 -1
- package/gen/license.d.ts.map +1 -1
- package/gen/license.js.map +1 -1
- package/gen/mcpserver.d.ts.map +1 -1
- package/gen/mcpserver.js.map +1 -1
- package/gen/memory.d.ts.map +1 -1
- package/gen/memory.js.map +1 -1
- package/gen/oauthapp.d.ts.map +1 -1
- package/gen/oauthapp.js.map +1 -1
- package/gen/organization.d.ts.map +1 -1
- package/gen/organization.js.map +1 -1
- package/gen/plan.d.ts.map +1 -1
- package/gen/plan.js.map +1 -1
- package/gen/platformclient.d.ts.map +1 -1
- package/gen/platformclient.js.map +1 -1
- package/gen/plugin.d.ts.map +1 -1
- package/gen/plugin.js.map +1 -1
- package/gen/proto-utils.d.ts.map +1 -1
- package/gen/proto-utils.js.map +1 -1
- package/gen/resource-availability.d.ts.map +1 -1
- package/gen/resource-availability.js.map +1 -1
- package/gen/schedule.d.ts.map +1 -1
- package/gen/schedule.js.map +1 -1
- package/gen/session.d.ts.map +1 -1
- package/gen/session.js.map +1 -1
- package/gen/skill.d.ts.map +1 -1
- package/gen/skill.js.map +1 -1
- package/gen/subscription.d.ts.map +1 -1
- package/gen/subscription.js.map +1 -1
- package/gen/team.d.ts.map +1 -1
- package/gen/team.js.map +1 -1
- package/gen/types.d.ts.map +1 -1
- package/gen/types.js.map +1 -1
- package/gen/workflow.d.ts.map +1 -1
- package/gen/workflow.js.map +1 -1
- package/gen/workflowexecution.d.ts.map +1 -1
- package/gen/workflowexecution.js.map +1 -1
- package/gen/workflowinstance.d.ts.map +1 -1
- package/gen/workflowinstance.js.map +1 -1
- package/github.d.ts.map +1 -1
- package/github.js.map +1 -1
- package/guest-auth.d.ts.map +1 -1
- package/guest-auth.js.map +1 -1
- package/iam-principal.d.ts +6 -0
- package/iam-principal.d.ts.map +1 -1
- package/iam-principal.js +19 -0
- package/iam-principal.js.map +1 -1
- package/iam-role.d.ts.map +1 -1
- package/iam-role.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/internal/interceptors.d.ts.map +1 -1
- package/internal/interceptors.js.map +1 -1
- package/manifest/client.d.ts.map +1 -1
- package/manifest/client.js.map +1 -1
- package/manifest/document.d.ts.map +1 -1
- package/manifest/document.js.map +1 -1
- package/manifest/index.d.ts.map +1 -1
- package/manifest/index.js.map +1 -1
- package/manifest/parse.d.ts.map +1 -1
- package/manifest/parse.js.map +1 -1
- package/manifest/redaction.d.ts.map +1 -1
- package/manifest/redaction.js.map +1 -1
- package/manifest/registry.d.ts.map +1 -1
- package/manifest/registry.js.map +1 -1
- package/manifest/serialize.d.ts.map +1 -1
- package/manifest/serialize.js.map +1 -1
- package/mcpserver-connect.d.ts.map +1 -1
- package/mcpserver-connect.js.map +1 -1
- package/node.d.ts.map +1 -1
- package/node.js.map +1 -1
- package/package.json +2 -2
- package/platform-client-auth.d.ts.map +1 -1
- package/platform-client-auth.js.map +1 -1
- package/platform.d.ts.map +1 -1
- package/platform.js.map +1 -1
- package/plugin.d.ts.map +1 -1
- package/plugin.js.map +1 -1
- package/provider-standing.d.ts.map +1 -1
- package/provider-standing.js.map +1 -1
- package/resource-availability.d.ts.map +1 -1
- package/resource-availability.js.map +1 -1
- package/runner-adapter.d.ts.map +1 -1
- package/runner-adapter.js.map +1 -1
- package/search.d.ts.map +1 -1
- package/search.js.map +1 -1
- package/session.d.ts.map +1 -1
- package/session.js.map +1 -1
- package/sharing.d.ts.map +1 -1
- package/sharing.js.map +1 -1
- package/skill.d.ts.map +1 -1
- package/skill.js.map +1 -1
- package/src/gen/authorization-config.ts +2 -2
- package/src/iam-principal.ts +24 -0
- package/src/index.ts +1 -0
- package/stigmer.d.ts.map +1 -1
- package/stigmer.js.map +1 -1
- package/transport.d.ts.map +1 -1
- package/transport.js.map +1 -1
- package/__tests__/billing.test.d.ts +0 -2
- package/__tests__/billing.test.d.ts.map +0 -1
- package/__tests__/billing.test.js +0 -120
- package/__tests__/billing.test.js.map +0 -1
- package/__tests__/config.test.d.ts +0 -2
- package/__tests__/config.test.d.ts.map +0 -1
- package/__tests__/config.test.js +0 -35
- package/__tests__/config.test.js.map +0 -1
- package/__tests__/ensure-identity-account.test.d.ts +0 -2
- package/__tests__/ensure-identity-account.test.d.ts.map +0 -1
- package/__tests__/ensure-identity-account.test.js +0 -108
- package/__tests__/ensure-identity-account.test.js.map +0 -1
- package/__tests__/errors.test.d.ts +0 -2
- package/__tests__/errors.test.d.ts.map +0 -1
- package/__tests__/errors.test.js +0 -228
- package/__tests__/errors.test.js.map +0 -1
- package/__tests__/gen/errors.test.d.ts +0 -2
- package/__tests__/gen/errors.test.d.ts.map +0 -1
- package/__tests__/gen/errors.test.js +0 -97
- package/__tests__/gen/errors.test.js.map +0 -1
- package/__tests__/gen/invitation-client.test.d.ts +0 -2
- package/__tests__/gen/invitation-client.test.d.ts.map +0 -1
- package/__tests__/gen/invitation-client.test.js +0 -106
- package/__tests__/gen/invitation-client.test.js.map +0 -1
- package/__tests__/gen/proto-utils.test.d.ts +0 -2
- package/__tests__/gen/proto-utils.test.d.ts.map +0 -1
- package/__tests__/gen/proto-utils.test.js +0 -85
- package/__tests__/gen/proto-utils.test.js.map +0 -1
- package/__tests__/gen/session-client.test.d.ts +0 -2
- package/__tests__/gen/session-client.test.d.ts.map +0 -1
- package/__tests__/gen/session-client.test.js +0 -152
- package/__tests__/gen/session-client.test.js.map +0 -1
- package/__tests__/guest-auth.test.d.ts +0 -2
- package/__tests__/guest-auth.test.d.ts.map +0 -1
- package/__tests__/guest-auth.test.js +0 -223
- package/__tests__/guest-auth.test.js.map +0 -1
- package/__tests__/iam-principal.test.d.ts +0 -2
- package/__tests__/iam-principal.test.d.ts.map +0 -1
- package/__tests__/iam-principal.test.js +0 -65
- package/__tests__/iam-principal.test.js.map +0 -1
- package/__tests__/manifest.test.d.ts +0 -2
- package/__tests__/manifest.test.d.ts.map +0 -1
- package/__tests__/manifest.test.js +0 -306
- package/__tests__/manifest.test.js.map +0 -1
- package/__tests__/mcpserver-connect.test.d.ts +0 -2
- package/__tests__/mcpserver-connect.test.d.ts.map +0 -1
- package/__tests__/mcpserver-connect.test.js +0 -146
- package/__tests__/mcpserver-connect.test.js.map +0 -1
- package/__tests__/node.test.d.ts +0 -2
- package/__tests__/node.test.d.ts.map +0 -1
- package/__tests__/node.test.js +0 -67
- package/__tests__/node.test.js.map +0 -1
- package/__tests__/platform-client-auth.test.d.ts +0 -2
- package/__tests__/platform-client-auth.test.d.ts.map +0 -1
- package/__tests__/platform-client-auth.test.js +0 -59
- package/__tests__/platform-client-auth.test.js.map +0 -1
- package/__tests__/platform.test.d.ts +0 -2
- package/__tests__/platform.test.d.ts.map +0 -1
- package/__tests__/platform.test.js +0 -52
- package/__tests__/platform.test.js.map +0 -1
- package/__tests__/plugin.test.d.ts +0 -2
- package/__tests__/plugin.test.d.ts.map +0 -1
- package/__tests__/plugin.test.js +0 -74
- package/__tests__/plugin.test.js.map +0 -1
- package/__tests__/resource-availability.test.d.ts +0 -2
- package/__tests__/resource-availability.test.d.ts.map +0 -1
- package/__tests__/resource-availability.test.js +0 -80
- package/__tests__/resource-availability.test.js.map +0 -1
- package/__tests__/runner-adapter.test.d.ts +0 -2
- package/__tests__/runner-adapter.test.d.ts.map +0 -1
- package/__tests__/runner-adapter.test.js +0 -88
- package/__tests__/runner-adapter.test.js.map +0 -1
- package/__tests__/session.test.d.ts +0 -6
- package/__tests__/session.test.d.ts.map +0 -1
- package/__tests__/session.test.js +0 -50
- package/__tests__/session.test.js.map +0 -1
- package/__tests__/sharing.test.d.ts +0 -2
- package/__tests__/sharing.test.d.ts.map +0 -1
- package/__tests__/sharing.test.js +0 -106
- package/__tests__/sharing.test.js.map +0 -1
- package/__tests__/skill.test.d.ts +0 -2
- package/__tests__/skill.test.d.ts.map +0 -1
- package/__tests__/skill.test.js +0 -117
- package/__tests__/skill.test.js.map +0 -1
- package/__tests__/stigmer.test.d.ts +0 -2
- package/__tests__/stigmer.test.d.ts.map +0 -1
- package/__tests__/stigmer.test.js +0 -127
- package/__tests__/stigmer.test.js.map +0 -1
- package/__tests__/update-input-roundtrip.test.d.ts +0 -2
- package/__tests__/update-input-roundtrip.test.d.ts.map +0 -1
- package/__tests__/update-input-roundtrip.test.js +0 -775
- package/__tests__/update-input-roundtrip.test.js.map +0 -1
- package/__tests__/update-input.test.d.ts +0 -2
- package/__tests__/update-input.test.d.ts.map +0 -1
- package/__tests__/update-input.test.js +0 -188
- package/__tests__/update-input.test.js.map +0 -1
- package/execution/__tests__/approval-provenance.test.d.ts +0 -2
- package/execution/__tests__/approval-provenance.test.d.ts.map +0 -1
- package/execution/__tests__/approval-provenance.test.js +0 -42
- package/execution/__tests__/approval-provenance.test.js.map +0 -1
- package/execution/__tests__/file-review-fold.corpus.test.d.ts +0 -2
- package/execution/__tests__/file-review-fold.corpus.test.d.ts.map +0 -1
- package/execution/__tests__/file-review-fold.corpus.test.js +0 -82
- package/execution/__tests__/file-review-fold.corpus.test.js.map +0 -1
- package/execution/__tests__/file-review-fold.test.d.ts +0 -2
- package/execution/__tests__/file-review-fold.test.d.ts.map +0 -1
- package/execution/__tests__/file-review-fold.test.js +0 -184
- package/execution/__tests__/file-review-fold.test.js.map +0 -1
- package/execution/__tests__/to-display-file-change.test.d.ts +0 -2
- package/execution/__tests__/to-display-file-change.test.d.ts.map +0 -1
- package/execution/__tests__/to-display-file-change.test.js +0 -81
- package/execution/__tests__/to-display-file-change.test.js.map +0 -1
- package/execution/__tests__/tool-view.file-changes.test.d.ts +0 -2
- package/execution/__tests__/tool-view.file-changes.test.d.ts.map +0 -1
- package/execution/__tests__/tool-view.file-changes.test.js +0 -86
- package/execution/__tests__/tool-view.file-changes.test.js.map +0 -1
- package/execution/__tests__/tool-view.fixtures.test.d.ts +0 -2
- package/execution/__tests__/tool-view.fixtures.test.d.ts.map +0 -1
- package/execution/__tests__/tool-view.fixtures.test.js +0 -152
- package/execution/__tests__/tool-view.fixtures.test.js.map +0 -1
- package/execution/__tests__/tool-view.output-ref.test.d.ts +0 -2
- package/execution/__tests__/tool-view.output-ref.test.d.ts.map +0 -1
- package/execution/__tests__/tool-view.output-ref.test.js +0 -64
- package/execution/__tests__/tool-view.output-ref.test.js.map +0 -1
- package/execution/__tests__/tool-view.read.test.d.ts +0 -2
- package/execution/__tests__/tool-view.read.test.d.ts.map +0 -1
- package/execution/__tests__/tool-view.read.test.js +0 -56
- package/execution/__tests__/tool-view.read.test.js.map +0 -1
- package/execution/__tests__/tool-view.search.test.d.ts +0 -2
- package/execution/__tests__/tool-view.search.test.d.ts.map +0 -1
- package/execution/__tests__/tool-view.search.test.js +0 -200
- package/execution/__tests__/tool-view.search.test.js.map +0 -1
- package/execution/__tests__/tool-view.shell.test.d.ts +0 -2
- package/execution/__tests__/tool-view.shell.test.d.ts.map +0 -1
- package/execution/__tests__/tool-view.shell.test.js +0 -104
- package/execution/__tests__/tool-view.shell.test.js.map +0 -1
- package/execution/__tests__/transcript.test.d.ts +0 -2
- package/execution/__tests__/transcript.test.d.ts.map +0 -1
- package/execution/__tests__/transcript.test.js +0 -591
- package/execution/__tests__/transcript.test.js.map +0 -1
- package/node.test.d.ts +0 -2
- package/node.test.d.ts.map +0 -1
- package/node.test.js +0 -35
- package/node.test.js.map +0 -1
- package/src/__tests__/billing.test.ts +0 -146
- package/src/__tests__/config.test.ts +0 -52
- package/src/__tests__/ensure-identity-account.test.ts +0 -146
- package/src/__tests__/errors.test.ts +0 -349
- package/src/__tests__/gen/errors.test.ts +0 -138
- package/src/__tests__/gen/invitation-client.test.ts +0 -132
- package/src/__tests__/gen/proto-utils.test.ts +0 -99
- package/src/__tests__/gen/session-client.test.ts +0 -208
- package/src/__tests__/guest-auth.test.ts +0 -287
- package/src/__tests__/iam-principal.test.ts +0 -82
- package/src/__tests__/manifest.test.ts +0 -362
- package/src/__tests__/mcpserver-connect.test.ts +0 -181
- package/src/__tests__/node.test.ts +0 -74
- package/src/__tests__/platform-client-auth.test.ts +0 -76
- package/src/__tests__/platform.test.ts +0 -74
- package/src/__tests__/plugin.test.ts +0 -92
- package/src/__tests__/resource-availability.test.ts +0 -118
- package/src/__tests__/runner-adapter.test.ts +0 -122
- package/src/__tests__/session.test.ts +0 -66
- package/src/__tests__/sharing.test.ts +0 -159
- package/src/__tests__/skill.test.ts +0 -150
- package/src/__tests__/stigmer.test.ts +0 -170
- package/src/__tests__/update-input-roundtrip.test.ts +0 -1007
- package/src/__tests__/update-input.test.ts +0 -250
- package/src/execution/__tests__/approval-provenance.test.ts +0 -60
- package/src/execution/__tests__/file-review-fold.corpus.test.ts +0 -127
- package/src/execution/__tests__/file-review-fold.test.ts +0 -281
- package/src/execution/__tests__/to-display-file-change.test.ts +0 -106
- package/src/execution/__tests__/tool-view.file-changes.test.ts +0 -100
- package/src/execution/__tests__/tool-view.fixtures.test.ts +0 -225
- package/src/execution/__tests__/tool-view.output-ref.test.ts +0 -76
- package/src/execution/__tests__/tool-view.read.test.ts +0 -71
- package/src/execution/__tests__/tool-view.search.test.ts +0 -264
- package/src/execution/__tests__/tool-view.shell.test.ts +0 -127
- package/src/execution/__tests__/transcript.golden.md +0 -100
- package/src/execution/__tests__/transcript.test.ts +0 -697
- package/src/node.test.ts +0 -38
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { ConnectError, Code } from "@connectrpc/connect";
|
|
3
|
-
import { ErrorInfoSchema } from "@stigmer/protos/google/rpc/error_details_pb";
|
|
4
|
-
import { StigmerError, type ErrorCode } from "../gen/errors";
|
|
5
|
-
import {
|
|
6
|
-
classifyError,
|
|
7
|
-
isRetryableError,
|
|
8
|
-
isTransientStreamError,
|
|
9
|
-
getUserMessage,
|
|
10
|
-
getErrorReason,
|
|
11
|
-
annotateRpcError,
|
|
12
|
-
getRpcMetadata,
|
|
13
|
-
type ErrorCategory,
|
|
14
|
-
} from "../errors";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Guest launch-gate refusal contract (shared-agent abuse controls).
|
|
18
|
-
*
|
|
19
|
-
* The cloud backend resolves owner-customizable refusal copy server-side and
|
|
20
|
-
* carries it in the gRPC status description; the SDK must surface that copy
|
|
21
|
-
* VERBATIM through getUserMessage — no client-side mapping exists by design.
|
|
22
|
-
* These tests pin the two halves of that contract:
|
|
23
|
-
*
|
|
24
|
-
* 1. RESOURCE_EXHAUSTED / FAILED_PRECONDITION descriptions pass through
|
|
25
|
-
* getUserMessage untouched.
|
|
26
|
-
* 2. The platform-default copy (mirrors GuestLimitReason in the cloud
|
|
27
|
-
* backend) never collides with the sanitizer's rewrite patterns.
|
|
28
|
-
*/
|
|
29
|
-
describe("guest launch-gate refusal copy passthrough", () => {
|
|
30
|
-
// Mirrors GuestLimitReason default copy in the cloud stigmer-service.
|
|
31
|
-
// If those strings change, update here — this guardrail exists to catch a
|
|
32
|
-
// default that a sanitizer pattern would silently rewrite.
|
|
33
|
-
const platformDefaultCopy = [
|
|
34
|
-
"You\u2019re sending messages too quickly. Please wait a moment before sending another.",
|
|
35
|
-
"This agent is currently unavailable. Please check back later.",
|
|
36
|
-
"This conversation has ended. Please start a new conversation to continue.",
|
|
37
|
-
"This agent can\u2019t be embedded on this site.",
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
const refusalCodes: Array<[string, Code]> = [
|
|
41
|
-
["ResourceExhausted (rate limit)", Code.ResourceExhausted],
|
|
42
|
-
["FailedPrecondition (fail-closed / bounds)", Code.FailedPrecondition],
|
|
43
|
-
["PermissionDenied (embed origin)", Code.PermissionDenied],
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
it.each(refusalCodes)(
|
|
47
|
-
"surfaces a %s status description verbatim",
|
|
48
|
-
(_label, code) => {
|
|
49
|
-
for (const copy of platformDefaultCopy) {
|
|
50
|
-
expect(getUserMessage(new ConnectError(copy, code))).toBe(copy);
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
it("surfaces owner-customized copy verbatim", () => {
|
|
56
|
-
const ownerCopy = "Whoa, slow down! Try again in a minute or two.";
|
|
57
|
-
expect(
|
|
58
|
-
getUserMessage(new ConnectError(ownerCopy, Code.ResourceExhausted)),
|
|
59
|
-
).toBe(ownerCopy);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("classifies rate-limit refusals as retryable and bounds refusals as not", () => {
|
|
63
|
-
expect(
|
|
64
|
-
isRetryableError(new ConnectError("copy", Code.ResourceExhausted)),
|
|
65
|
-
).toBe(true);
|
|
66
|
-
expect(
|
|
67
|
-
isRetryableError(new ConnectError("copy", Code.FailedPrecondition)),
|
|
68
|
-
).toBe(false);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe("classifyError", () => {
|
|
73
|
-
const stigmerMappings: Array<[string, ErrorCategory]> = [
|
|
74
|
-
["unauthenticated", "auth"],
|
|
75
|
-
["permission-denied", "permission"],
|
|
76
|
-
["not-found", "not-found"],
|
|
77
|
-
["invalid-argument", "validation"],
|
|
78
|
-
["already-exists", "validation"],
|
|
79
|
-
["failed-precondition", "validation"],
|
|
80
|
-
["resource-exhausted", "unavailable"],
|
|
81
|
-
["internal", "server"],
|
|
82
|
-
["unavailable", "unavailable"],
|
|
83
|
-
["cancelled", "cancelled"],
|
|
84
|
-
["unknown", "server"],
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
it.each(stigmerMappings)(
|
|
88
|
-
"maps StigmerError code '%s' to category '%s'",
|
|
89
|
-
(errorCode, expectedCategory) => {
|
|
90
|
-
const result = classifyError(
|
|
91
|
-
new StigmerError(errorCode as ErrorCode, "test", Code.Unknown),
|
|
92
|
-
);
|
|
93
|
-
expect(result).toBe(expectedCategory);
|
|
94
|
-
},
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const connectMappings: Array<[Code, ErrorCategory]> = [
|
|
98
|
-
[Code.Unauthenticated, "auth"],
|
|
99
|
-
[Code.PermissionDenied, "permission"],
|
|
100
|
-
[Code.NotFound, "not-found"],
|
|
101
|
-
[Code.InvalidArgument, "validation"],
|
|
102
|
-
[Code.FailedPrecondition, "validation"],
|
|
103
|
-
[Code.OutOfRange, "validation"],
|
|
104
|
-
[Code.AlreadyExists, "validation"],
|
|
105
|
-
[Code.Aborted, "validation"],
|
|
106
|
-
[Code.Internal, "server"],
|
|
107
|
-
[Code.Unknown, "server"],
|
|
108
|
-
[Code.DataLoss, "server"],
|
|
109
|
-
[Code.Unimplemented, "server"],
|
|
110
|
-
[Code.Unavailable, "unavailable"],
|
|
111
|
-
[Code.DeadlineExceeded, "unavailable"],
|
|
112
|
-
[Code.ResourceExhausted, "unavailable"],
|
|
113
|
-
[Code.Canceled, "cancelled"],
|
|
114
|
-
];
|
|
115
|
-
|
|
116
|
-
it.each(connectMappings)(
|
|
117
|
-
"maps ConnectError code %i to category '%s'",
|
|
118
|
-
(code, expectedCategory) => {
|
|
119
|
-
const result = classifyError(new ConnectError("test", code));
|
|
120
|
-
expect(result).toBe(expectedCategory);
|
|
121
|
-
},
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
it("returns 'unknown' for plain Error", () => {
|
|
125
|
-
expect(classifyError(new Error("random"))).toBe("unknown");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("returns 'unknown' for string", () => {
|
|
129
|
-
expect(classifyError("just a string")).toBe("unknown");
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("returns 'unknown' for null", () => {
|
|
133
|
-
expect(classifyError(null)).toBe("unknown");
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe("isRetryableError", () => {
|
|
138
|
-
it("returns true for 'server' category errors", () => {
|
|
139
|
-
expect(
|
|
140
|
-
isRetryableError(new StigmerError("internal", "boom", Code.Internal)),
|
|
141
|
-
).toBe(true);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("returns true for 'unavailable' category errors", () => {
|
|
145
|
-
expect(
|
|
146
|
-
isRetryableError(
|
|
147
|
-
new StigmerError("unavailable", "down", Code.Unavailable),
|
|
148
|
-
),
|
|
149
|
-
).toBe(true);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("returns false for 'auth' category errors", () => {
|
|
153
|
-
expect(
|
|
154
|
-
isRetryableError(
|
|
155
|
-
new StigmerError("unauthenticated", "bad token", Code.Unauthenticated),
|
|
156
|
-
),
|
|
157
|
-
).toBe(false);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("returns false for 'not-found' category errors", () => {
|
|
161
|
-
expect(
|
|
162
|
-
isRetryableError(
|
|
163
|
-
new StigmerError("not-found", "gone", Code.NotFound),
|
|
164
|
-
),
|
|
165
|
-
).toBe(false);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("returns false for plain Error", () => {
|
|
169
|
-
expect(isRetryableError(new Error("oops"))).toBe(false);
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
describe("isTransientStreamError", () => {
|
|
174
|
-
it("is true for retryable category errors (server / unavailable)", () => {
|
|
175
|
-
expect(
|
|
176
|
-
isTransientStreamError(
|
|
177
|
-
new StigmerError("unavailable", "down", Code.Unavailable),
|
|
178
|
-
),
|
|
179
|
-
).toBe(true);
|
|
180
|
-
expect(
|
|
181
|
-
isTransientStreamError(new ConnectError("boom", Code.Internal)),
|
|
182
|
-
).toBe(true);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// The crux of #174: WebKit (Safari / WKWebView) phrases a failed fetch as a
|
|
186
|
-
// bare `TypeError: Load failed`, which classifies as `unknown` (not
|
|
187
|
-
// retryable) — yet it is exactly the transient drop we must auto-reconnect.
|
|
188
|
-
it.each([
|
|
189
|
-
"Load failed",
|
|
190
|
-
"TypeError: Load failed",
|
|
191
|
-
"Failed to fetch",
|
|
192
|
-
"fetch failed",
|
|
193
|
-
"read ECONNRESET",
|
|
194
|
-
"connect ETIMEDOUT",
|
|
195
|
-
])("is true for transport noise %j (even as a bare TypeError)", (message) => {
|
|
196
|
-
expect(isTransientStreamError(new TypeError(message))).toBe(true);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it.each([
|
|
200
|
-
new StigmerError("not-found", "gone", Code.NotFound),
|
|
201
|
-
new StigmerError("invalid-argument", "bad", Code.InvalidArgument),
|
|
202
|
-
new StigmerError("unauthenticated", "no token", Code.Unauthenticated),
|
|
203
|
-
new Error("some deterministic application error"),
|
|
204
|
-
])("is false for deterministic / unknown errors", (error) => {
|
|
205
|
-
expect(isTransientStreamError(error)).toBe(false);
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
describe("getUserMessage", () => {
|
|
210
|
-
it("extracts message from StigmerError", () => {
|
|
211
|
-
const err = new StigmerError("not-found", "Agent not found", Code.NotFound);
|
|
212
|
-
expect(getUserMessage(err)).toBe("Agent not found");
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("extracts rawMessage from ConnectError", () => {
|
|
216
|
-
const err = new ConnectError("Agent not found", Code.NotFound);
|
|
217
|
-
expect(getUserMessage(err)).toBe("Agent not found");
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("extracts message from plain Error", () => {
|
|
221
|
-
expect(getUserMessage(new Error("something"))).toBe("something");
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("extracts message from string", () => {
|
|
225
|
-
expect(getUserMessage("a string error")).toBe("a string error");
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("sanitizes 'no healthy upstream' to readable message", () => {
|
|
229
|
-
const err = new StigmerError(
|
|
230
|
-
"unavailable",
|
|
231
|
-
"no healthy upstream",
|
|
232
|
-
Code.Unavailable,
|
|
233
|
-
);
|
|
234
|
-
expect(getUserMessage(err)).toBe(
|
|
235
|
-
"The server is temporarily unavailable.",
|
|
236
|
-
);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it("sanitizes 'ECONNREFUSED'", () => {
|
|
240
|
-
expect(getUserMessage(new Error("connect ECONNREFUSED 127.0.0.1"))).toBe(
|
|
241
|
-
"Unable to connect to the server.",
|
|
242
|
-
);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("sanitizes 'fetch failed'", () => {
|
|
246
|
-
expect(getUserMessage(new Error("fetch failed"))).toBe(
|
|
247
|
-
"Unable to reach the server. Check your connection.",
|
|
248
|
-
);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it("sanitizes WebKit's 'Load failed' (Safari / WKWebView)", () => {
|
|
252
|
-
expect(getUserMessage(new TypeError("Load failed"))).toBe(
|
|
253
|
-
"The connection to the server was lost.",
|
|
254
|
-
);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it("uses category fallback when message is empty", () => {
|
|
258
|
-
const err = new StigmerError("not-found", "", Code.NotFound);
|
|
259
|
-
expect(getUserMessage(err)).toBe("The requested resource was not found.");
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("uses custom fallback when provided and message is empty", () => {
|
|
263
|
-
const err = new StigmerError("not-found", "", Code.NotFound);
|
|
264
|
-
expect(getUserMessage(err, "Custom fallback")).toBe("Custom fallback");
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it("uses 'unknown' category fallback for non-SDK errors without message", () => {
|
|
268
|
-
expect(getUserMessage(42)).toBe("An unexpected error occurred.");
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
describe("getErrorReason", () => {
|
|
273
|
-
const errorInfoDetail = {
|
|
274
|
-
desc: ErrorInfoSchema,
|
|
275
|
-
value: {
|
|
276
|
-
domain: "stigmer.ai",
|
|
277
|
-
reason: "SLACK_WORKSPACE_ALREADY_CONNECTED",
|
|
278
|
-
metadata: { team_name: "Acme HQ", channel_app_id: "" },
|
|
279
|
-
},
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
it("extracts the reason from a ConnectError's ErrorInfo detail", () => {
|
|
283
|
-
const error = new ConnectError(
|
|
284
|
-
"workspace taken",
|
|
285
|
-
Code.FailedPrecondition,
|
|
286
|
-
undefined,
|
|
287
|
-
[errorInfoDetail],
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
expect(getErrorReason(error)).toEqual({
|
|
291
|
-
reason: "SLACK_WORKSPACE_ALREADY_CONNECTED",
|
|
292
|
-
domain: "stigmer.ai",
|
|
293
|
-
metadata: { team_name: "Acme HQ", channel_app_id: "" },
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it("walks the cause chain of an SDK-wrapped StigmerError", () => {
|
|
298
|
-
const connectError = new ConnectError(
|
|
299
|
-
"workspace taken",
|
|
300
|
-
Code.FailedPrecondition,
|
|
301
|
-
undefined,
|
|
302
|
-
[errorInfoDetail],
|
|
303
|
-
);
|
|
304
|
-
const wrapped = new StigmerError(
|
|
305
|
-
"failed-precondition",
|
|
306
|
-
connectError.rawMessage,
|
|
307
|
-
Code.FailedPrecondition,
|
|
308
|
-
{ cause: connectError },
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
expect(getErrorReason(wrapped)?.reason).toBe(
|
|
312
|
-
"SLACK_WORKSPACE_ALREADY_CONNECTED",
|
|
313
|
-
);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it("returns null for errors without an ErrorInfo detail", () => {
|
|
317
|
-
expect(
|
|
318
|
-
getErrorReason(new ConnectError("bare refusal", Code.FailedPrecondition)),
|
|
319
|
-
).toBeNull();
|
|
320
|
-
expect(
|
|
321
|
-
getErrorReason(
|
|
322
|
-
new StigmerError("failed-precondition", "bare refusal", 9),
|
|
323
|
-
),
|
|
324
|
-
).toBeNull();
|
|
325
|
-
expect(getErrorReason(new Error("plain"))).toBeNull();
|
|
326
|
-
expect(getErrorReason("not an error")).toBeNull();
|
|
327
|
-
expect(getErrorReason(null)).toBeNull();
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
describe("annotateRpcError / getRpcMetadata", () => {
|
|
333
|
-
it("roundtrips metadata on an error object", () => {
|
|
334
|
-
const err = new Error("test");
|
|
335
|
-
const metadata = { method: "Get", path: "/api/agent" };
|
|
336
|
-
annotateRpcError(err, metadata);
|
|
337
|
-
expect(getRpcMetadata(err)).toEqual(metadata);
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it("returns undefined for unannotated errors", () => {
|
|
341
|
-
expect(getRpcMetadata(new Error("no metadata"))).toBeUndefined();
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
it("returns undefined for non-object errors", () => {
|
|
345
|
-
expect(getRpcMetadata("string error")).toBeUndefined();
|
|
346
|
-
expect(getRpcMetadata(null)).toBeUndefined();
|
|
347
|
-
expect(getRpcMetadata(42)).toBeUndefined();
|
|
348
|
-
});
|
|
349
|
-
});
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { ConnectError, Code } from "@connectrpc/connect";
|
|
3
|
-
import {
|
|
4
|
-
StigmerError,
|
|
5
|
-
wrapError,
|
|
6
|
-
isNotFound,
|
|
7
|
-
isUnauthenticated,
|
|
8
|
-
isPermissionDenied,
|
|
9
|
-
isRetryable,
|
|
10
|
-
} from "../../gen/errors";
|
|
11
|
-
|
|
12
|
-
describe("StigmerError", () => {
|
|
13
|
-
it("has name 'StigmerError'", () => {
|
|
14
|
-
const err = new StigmerError("not-found", "resource missing", Code.NotFound);
|
|
15
|
-
expect(err.name).toBe("StigmerError");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("inherits from Error", () => {
|
|
19
|
-
const err = new StigmerError("internal", "boom", Code.Internal);
|
|
20
|
-
expect(err).toBeInstanceOf(Error);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("exposes code and connectCode", () => {
|
|
24
|
-
const err = new StigmerError("unauthenticated", "bad token", Code.Unauthenticated);
|
|
25
|
-
expect(err.code).toBe("unauthenticated");
|
|
26
|
-
expect(err.connectCode).toBe(Code.Unauthenticated);
|
|
27
|
-
expect(err.message).toBe("bad token");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("wrapError", () => {
|
|
32
|
-
const connectCodeMappings: Array<[Code, string]> = [
|
|
33
|
-
[Code.NotFound, "not-found"],
|
|
34
|
-
[Code.PermissionDenied, "permission-denied"],
|
|
35
|
-
[Code.Unauthenticated, "unauthenticated"],
|
|
36
|
-
[Code.InvalidArgument, "invalid-argument"],
|
|
37
|
-
[Code.AlreadyExists, "already-exists"],
|
|
38
|
-
[Code.ResourceExhausted, "resource-exhausted"],
|
|
39
|
-
[Code.FailedPrecondition, "failed-precondition"],
|
|
40
|
-
[Code.Internal, "internal"],
|
|
41
|
-
[Code.Unavailable, "unavailable"],
|
|
42
|
-
[Code.Canceled, "cancelled"],
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
it.each(connectCodeMappings)(
|
|
46
|
-
"maps ConnectError code %i to ErrorCode '%s'",
|
|
47
|
-
(connectCode, expectedErrorCode) => {
|
|
48
|
-
const connectErr = new ConnectError("test", connectCode);
|
|
49
|
-
const result = wrapError(connectErr);
|
|
50
|
-
expect(result).toBeInstanceOf(StigmerError);
|
|
51
|
-
expect(result.code).toBe(expectedErrorCode);
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
it("maps unmapped ConnectError codes to 'unknown'", () => {
|
|
56
|
-
const connectErr = new ConnectError("test", 0 as unknown as Code);
|
|
57
|
-
const result = wrapError(connectErr);
|
|
58
|
-
expect(result.code).toBe("unknown");
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("passes through StigmerError unchanged", () => {
|
|
62
|
-
const original = new StigmerError("not-found", "gone", Code.NotFound);
|
|
63
|
-
const result = wrapError(original);
|
|
64
|
-
expect(result).toBe(original);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("wraps plain Error as 'unknown'", () => {
|
|
68
|
-
const result = wrapError(new Error("something broke"));
|
|
69
|
-
expect(result).toBeInstanceOf(StigmerError);
|
|
70
|
-
expect(result.code).toBe("unknown");
|
|
71
|
-
expect(result.message).toBe("something broke");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("wraps string as 'unknown'", () => {
|
|
75
|
-
const result = wrapError("raw string error");
|
|
76
|
-
expect(result).toBeInstanceOf(StigmerError);
|
|
77
|
-
expect(result.code).toBe("unknown");
|
|
78
|
-
expect(result.message).toBe("raw string error");
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe("predicates", () => {
|
|
83
|
-
it("isNotFound returns true for not-found StigmerError", () => {
|
|
84
|
-
expect(isNotFound(new StigmerError("not-found", "x", Code.NotFound))).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("isNotFound returns false for other codes", () => {
|
|
88
|
-
expect(isNotFound(new StigmerError("internal", "x", Code.Internal))).toBe(false);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("isNotFound returns false for non-StigmerError", () => {
|
|
92
|
-
expect(isNotFound(new Error("not found"))).toBe(false);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("isUnauthenticated returns true for unauthenticated StigmerError", () => {
|
|
96
|
-
expect(
|
|
97
|
-
isUnauthenticated(new StigmerError("unauthenticated", "x", Code.Unauthenticated)),
|
|
98
|
-
).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("isUnauthenticated returns false for other codes", () => {
|
|
102
|
-
expect(
|
|
103
|
-
isUnauthenticated(new StigmerError("not-found", "x", Code.NotFound)),
|
|
104
|
-
).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("isPermissionDenied returns true for permission-denied StigmerError", () => {
|
|
108
|
-
expect(
|
|
109
|
-
isPermissionDenied(
|
|
110
|
-
new StigmerError("permission-denied", "x", Code.PermissionDenied),
|
|
111
|
-
),
|
|
112
|
-
).toBe(true);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("isPermissionDenied returns false for other codes", () => {
|
|
116
|
-
expect(
|
|
117
|
-
isPermissionDenied(new StigmerError("internal", "x", Code.Internal)),
|
|
118
|
-
).toBe(false);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("isRetryable returns true for internal", () => {
|
|
122
|
-
expect(isRetryable(new StigmerError("internal", "x", Code.Internal))).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("isRetryable returns true for unavailable", () => {
|
|
126
|
-
expect(isRetryable(new StigmerError("unavailable", "x", Code.Unavailable))).toBe(
|
|
127
|
-
true,
|
|
128
|
-
);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("isRetryable returns false for not-found", () => {
|
|
132
|
-
expect(isRetryable(new StigmerError("not-found", "x", Code.NotFound))).toBe(false);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("isRetryable returns false for non-StigmerError", () => {
|
|
136
|
-
expect(isRetryable(new Error("server error"))).toBe(false);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { create, toBinary, fromBinary } from "@bufbuild/protobuf";
|
|
3
|
-
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
4
|
-
import { InvitationSchema } from "@stigmer/protos/ai/stigmer/iam/invitation/v1/api_pb";
|
|
5
|
-
import { InvitationSpecSchema } from "@stigmer/protos/ai/stigmer/iam/invitation/v1/spec_pb";
|
|
6
|
-
import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
7
|
-
import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
8
|
-
import { stripUndefined, toTimestamp } from "../../gen/proto-utils";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Exercises the same code path as the generated `buildInvitationProto` to
|
|
12
|
-
* verify that Timestamp fields serialize without error after the codegen fix.
|
|
13
|
-
*/
|
|
14
|
-
function buildInvitationProto(input: {
|
|
15
|
-
name: string;
|
|
16
|
-
org: string;
|
|
17
|
-
role: IamRole;
|
|
18
|
-
expiresAt: Date | string;
|
|
19
|
-
maxRedemptions?: number;
|
|
20
|
-
label?: string;
|
|
21
|
-
}) {
|
|
22
|
-
const expiresAt = input.expiresAt !== undefined ? toTimestamp(input.expiresAt) : undefined;
|
|
23
|
-
return Object.assign(create(InvitationSchema), {
|
|
24
|
-
apiVersion: "iam.stigmer.ai/v1",
|
|
25
|
-
kind: "Invitation",
|
|
26
|
-
metadata: Object.assign(create(ApiResourceMetadataSchema), {
|
|
27
|
-
name: input.name,
|
|
28
|
-
org: input.org,
|
|
29
|
-
}),
|
|
30
|
-
spec: Object.assign(create(InvitationSpecSchema), stripUndefined({
|
|
31
|
-
role: input.role,
|
|
32
|
-
maxRedemptions: input.maxRedemptions,
|
|
33
|
-
expiresAt,
|
|
34
|
-
label: input.label,
|
|
35
|
-
})),
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe("buildInvitationProto", () => {
|
|
40
|
-
it("produces a message with a valid Timestamp from a Date", () => {
|
|
41
|
-
const expiresAt = new Date("2026-06-19T12:00:00.000Z");
|
|
42
|
-
const msg = buildInvitationProto({
|
|
43
|
-
name: "test-invite",
|
|
44
|
-
org: "acme",
|
|
45
|
-
role: IamRole.viewer,
|
|
46
|
-
expiresAt,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
expect(msg.spec?.expiresAt).toBeDefined();
|
|
50
|
-
expect(msg.spec!.expiresAt!.seconds).toBe(
|
|
51
|
-
BigInt(Math.floor(expiresAt.getTime() / 1000)),
|
|
52
|
-
);
|
|
53
|
-
expect(timestampDate(msg.spec!.expiresAt!).getTime()).toBe(
|
|
54
|
-
expiresAt.getTime(),
|
|
55
|
-
);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("produces a message with a valid Timestamp from an ISO string", () => {
|
|
59
|
-
const isoString = "2026-07-01T00:00:00.000Z";
|
|
60
|
-
const msg = buildInvitationProto({
|
|
61
|
-
name: "string-invite",
|
|
62
|
-
org: "acme",
|
|
63
|
-
role: IamRole.member,
|
|
64
|
-
expiresAt: isoString,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const expected = new Date(isoString);
|
|
68
|
-
expect(msg.spec?.expiresAt).toBeDefined();
|
|
69
|
-
expect(msg.spec!.expiresAt!.seconds).toBe(
|
|
70
|
-
BigInt(Math.floor(expected.getTime() / 1000)),
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("serializes to binary without error (the original bug)", () => {
|
|
75
|
-
const msg = buildInvitationProto({
|
|
76
|
-
name: "binary-test",
|
|
77
|
-
org: "acme",
|
|
78
|
-
role: IamRole.admin,
|
|
79
|
-
expiresAt: new Date(Date.now() + 7 * 86_400_000),
|
|
80
|
-
maxRedemptions: 1,
|
|
81
|
-
label: "test label",
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const bytes = toBinary(InvitationSchema, msg);
|
|
85
|
-
expect(bytes.length).toBeGreaterThan(0);
|
|
86
|
-
|
|
87
|
-
const decoded = fromBinary(InvitationSchema, bytes);
|
|
88
|
-
expect(decoded.spec?.expiresAt).toBeDefined();
|
|
89
|
-
expect(decoded.spec!.expiresAt!.seconds).toBe(msg.spec!.expiresAt!.seconds);
|
|
90
|
-
expect(decoded.spec?.role).toBe(IamRole.admin);
|
|
91
|
-
expect(decoded.spec?.maxRedemptions).toBe(1);
|
|
92
|
-
expect(decoded.spec?.label).toBe("test label");
|
|
93
|
-
expect(decoded.metadata?.name).toBe("binary-test");
|
|
94
|
-
expect(decoded.metadata?.org).toBe("acme");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("handles 7-day expiry (matches UI default options)", () => {
|
|
98
|
-
const now = Date.now();
|
|
99
|
-
const sevenDays = new Date(now + 7 * 86_400_000);
|
|
100
|
-
const msg = buildInvitationProto({
|
|
101
|
-
name: "7day",
|
|
102
|
-
org: "test-org",
|
|
103
|
-
role: IamRole.viewer,
|
|
104
|
-
expiresAt: sevenDays,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const bytes = toBinary(InvitationSchema, msg);
|
|
108
|
-
const decoded = fromBinary(InvitationSchema, bytes);
|
|
109
|
-
const decodedDate = timestampDate(decoded.spec!.expiresAt!);
|
|
110
|
-
|
|
111
|
-
const diffMs = Math.abs(decodedDate.getTime() - sevenDays.getTime());
|
|
112
|
-
expect(diffMs).toBeLessThan(1000);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("handles 30-day expiry (matches UI default options)", () => {
|
|
116
|
-
const now = Date.now();
|
|
117
|
-
const thirtyDays = new Date(now + 30 * 86_400_000);
|
|
118
|
-
const msg = buildInvitationProto({
|
|
119
|
-
name: "30day",
|
|
120
|
-
org: "test-org",
|
|
121
|
-
role: IamRole.owner,
|
|
122
|
-
expiresAt: thirtyDays,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const bytes = toBinary(InvitationSchema, msg);
|
|
126
|
-
const decoded = fromBinary(InvitationSchema, bytes);
|
|
127
|
-
const decodedDate = timestampDate(decoded.spec!.expiresAt!);
|
|
128
|
-
|
|
129
|
-
const diffMs = Math.abs(decodedDate.getTime() - thirtyDays.getTime());
|
|
130
|
-
expect(diffMs).toBeLessThan(1000);
|
|
131
|
-
});
|
|
132
|
-
});
|