From 9bc6e910078b3f550106c6a003413396597dc188 Mon Sep 17 00:00:00 2001 From: William Chong Date: Mon, 3 Feb 2025 11:25:38 +0400 Subject: [PATCH] test --- packages/db-client/src/Client/index.ts | 47 +- packages/test/src/connection/Channel.test.ts | 47 - .../__snapshots__/keepAlive.test.ts.snap | 89 -- .../parseConnectionString.test.ts.snap | 67 -- .../__snapshots__/tlsCAFile.test.ts.snap | 3 - packages/test/src/connection/cluster.test.ts | 108 --- .../src/connection/connectionName.test.ts | 31 - .../connectionString/connectionStringTests.ts | 83 -- .../connectionString/insecure-cluster.test.ts | 12 - .../insecure-single-node.test.ts | 11 - .../connectionString/secure-cluster.test.ts | 12 - .../secure-single-node.test.ts | 11 - .../deadline-settings.test.ts.snap | 13 - .../deadline/deadline-effects.test.ts | 70 -- .../deadline/deadline-settings.test.ts | 141 --- .../src/connection/defaultCredentials.test.ts | 54 -- .../src/connection/determineBestNode.test.ts | 179 ---- packages/test/src/connection/dns.test.ts | 77 -- packages/test/src/connection/insecure.test.ts | 31 - .../test/src/connection/keepAlive.test.ts | 172 ---- .../test/src/connection/not-leader.test.ts | 79 -- .../connection/parseConnectionString.test.ts | 31 - .../parseConnectionStringMockups.ts | 545 ------------ .../reconnect/NotLeaderError.test.ts | 80 -- .../reconnect/UnavailableError.test.ts | 73 -- .../reconnect/all-nodes-down.test.ts | 189 ---- .../connection/reconnect/mid-stream.test.ts | 71 -- .../reconnect/no-reconnection.test.ts | 119 --- .../test/src/connection/singleNode.test.ts | 32 - .../test/src/connection/tlsCAFile.test.ts | 49 -- .../src/extra/RecordedEvent-created.test.ts | 46 - packages/test/src/extra/dispose.test.ts | 395 --------- packages/test/src/extra/encoding.test.ts | 81 -- packages/test/src/extra/http.test.ts | 156 ---- .../src/extra/http2-assertion-failure.test.ts | 64 -- .../test/src/extra/oversize-event.test.ts | 89 -- .../test/src/extra/typedEvents-more.test.ts | 319 ------- packages/test/src/extra/typedEvents.test.ts | 334 ------- .../test/src/extra/write-after-end.test.ts | 124 --- .../src/opentelemetry/instrumentation.test.ts | 540 ------------ .../createPersistentSubscriptionToAll.test.ts | 128 --- ...eatePersistentSubscriptionToStream.test.ts | 92 -- .../deletePersistentSubscriptionToAll.test.ts | 65 -- ...letePersistentSubscriptionToStream.test.ts | 42 - ...getPersistentSubscriptionToAllInfo.test.ts | 218 ----- ...PersistentSubscriptionToStreamInfo.test.ts | 210 ----- .../listAllPersistentSubscriptions.test.ts | 213 ----- .../listPersistentSubscriptionsToAll.test.ts | 193 ---- ...istPersistentSubscriptionsToStream.test.ts | 176 ---- .../replayParkedMessagesToAll.test.ts | 287 ------ .../replayParkedMessagesToStream.test.ts | 262 ------ ...artPersistentSubscriptionSubsystem.test.ts | 27 - ...ersistentSubscriptionToAll-filters.test.ts | 197 ----- ...cribeToPersistentSubscriptionToAll.test.ts | 765 ---------------- ...beToPersistentSubscriptionToStream.test.ts | 825 ------------------ .../updatePersistentSubscriptionToAll.test.ts | 72 -- ...datePersistentSubscriptionToStream.test.ts | 49 -- ...lient-certificates.deprecated.test.ts.snap | 29 - .../client-certificates.test.ts.snap | 9 - .../client-certificates.deprecated.test.ts | 160 ---- .../src/plugins/client-certificates.test.ts | 151 ---- .../src/projections/createProjection.test.ts | 125 --- .../src/projections/deleteProjection.test.ts | 103 --- .../src/projections/disableProjection.test.ts | 119 --- .../src/projections/enableProjection.test.ts | 76 -- .../projections/getProjectionResult.test.ts | 178 ---- .../projections/getProjectionState.test.ts | 176 ---- .../projections/getProjectionStatus.test.ts | 62 -- .../src/projections/listProjections.test.ts | 51 -- .../src/projections/resetProjection.test.ts | 55 -- .../src/projections/restartSubsystem.test.ts | 25 - .../src/projections/updateProjection.test.ts | 76 -- packages/test/src/samples/appending-events.ts | 207 ----- packages/test/src/samples/get-started.ts | 60 -- packages/test/src/samples/opentelemetry.ts | 104 --- .../src/samples/persistent-subscriptions.ts | 401 --------- .../test/src/samples/projection-management.ts | 370 -------- packages/test/src/samples/reading-events.ts | 237 ----- .../test/src/samples/server-side-filtering.ts | 113 --- .../src/samples/subscribing-to-streams.ts | 198 ----- .../test/src/samples/user-certificates.ts | 47 - .../appendToStream-batch-append-flood.test.ts | 51 -- .../appendToStream-batch-append.test.ts | 142 --- .../src/streams/appendToStream-errors.test.ts | 147 ---- .../test/src/streams/appendToStream.test.ts | 620 ------------- .../test/src/streams/deleteStream.test.ts | 149 ---- .../src/streams/getStreamMetadata.test.ts | 96 -- packages/test/src/streams/readAll.test.ts | 167 ---- packages/test/src/streams/readStream.test.ts | 28 +- .../src/streams/setStreamMetadata.test.ts | 159 ---- .../streams/subscribeToAll-filters.test.ts | 293 ------- .../test/src/streams/subscribeToAll.test.ts | 474 ---------- .../src/streams/subscribeToStream.test.ts | 258 ------ .../test/src/streams/systemStreams.test.ts | 38 - .../test/src/streams/tombstoneStream.test.ts | 153 ---- packages/test/src/utils/collect.ts | 18 +- 96 files changed, 63 insertions(+), 14357 deletions(-) delete mode 100644 packages/test/src/connection/Channel.test.ts delete mode 100644 packages/test/src/connection/__snapshots__/keepAlive.test.ts.snap delete mode 100644 packages/test/src/connection/__snapshots__/parseConnectionString.test.ts.snap delete mode 100644 packages/test/src/connection/__snapshots__/tlsCAFile.test.ts.snap delete mode 100644 packages/test/src/connection/cluster.test.ts delete mode 100644 packages/test/src/connection/connectionName.test.ts delete mode 100644 packages/test/src/connection/connectionString/connectionStringTests.ts delete mode 100644 packages/test/src/connection/connectionString/insecure-cluster.test.ts delete mode 100644 packages/test/src/connection/connectionString/insecure-single-node.test.ts delete mode 100644 packages/test/src/connection/connectionString/secure-cluster.test.ts delete mode 100644 packages/test/src/connection/connectionString/secure-single-node.test.ts delete mode 100644 packages/test/src/connection/deadline/__snapshots__/deadline-settings.test.ts.snap delete mode 100644 packages/test/src/connection/deadline/deadline-effects.test.ts delete mode 100644 packages/test/src/connection/deadline/deadline-settings.test.ts delete mode 100644 packages/test/src/connection/defaultCredentials.test.ts delete mode 100644 packages/test/src/connection/determineBestNode.test.ts delete mode 100644 packages/test/src/connection/dns.test.ts delete mode 100644 packages/test/src/connection/insecure.test.ts delete mode 100644 packages/test/src/connection/keepAlive.test.ts delete mode 100644 packages/test/src/connection/not-leader.test.ts delete mode 100644 packages/test/src/connection/parseConnectionString.test.ts delete mode 100644 packages/test/src/connection/parseConnectionStringMockups.ts delete mode 100644 packages/test/src/connection/reconnect/NotLeaderError.test.ts delete mode 100644 packages/test/src/connection/reconnect/UnavailableError.test.ts delete mode 100644 packages/test/src/connection/reconnect/all-nodes-down.test.ts delete mode 100644 packages/test/src/connection/reconnect/mid-stream.test.ts delete mode 100644 packages/test/src/connection/reconnect/no-reconnection.test.ts delete mode 100644 packages/test/src/connection/singleNode.test.ts delete mode 100644 packages/test/src/connection/tlsCAFile.test.ts delete mode 100644 packages/test/src/extra/RecordedEvent-created.test.ts delete mode 100644 packages/test/src/extra/dispose.test.ts delete mode 100644 packages/test/src/extra/encoding.test.ts delete mode 100644 packages/test/src/extra/http.test.ts delete mode 100644 packages/test/src/extra/http2-assertion-failure.test.ts delete mode 100644 packages/test/src/extra/oversize-event.test.ts delete mode 100644 packages/test/src/extra/typedEvents-more.test.ts delete mode 100644 packages/test/src/extra/typedEvents.test.ts delete mode 100644 packages/test/src/extra/write-after-end.test.ts delete mode 100644 packages/test/src/opentelemetry/instrumentation.test.ts delete mode 100644 packages/test/src/persistentSubscription/createPersistentSubscriptionToAll.test.ts delete mode 100644 packages/test/src/persistentSubscription/createPersistentSubscriptionToStream.test.ts delete mode 100644 packages/test/src/persistentSubscription/deletePersistentSubscriptionToAll.test.ts delete mode 100644 packages/test/src/persistentSubscription/deletePersistentSubscriptionToStream.test.ts delete mode 100644 packages/test/src/persistentSubscription/getPersistentSubscriptionToAllInfo.test.ts delete mode 100644 packages/test/src/persistentSubscription/getPersistentSubscriptionToStreamInfo.test.ts delete mode 100644 packages/test/src/persistentSubscription/listAllPersistentSubscriptions.test.ts delete mode 100644 packages/test/src/persistentSubscription/listPersistentSubscriptionsToAll.test.ts delete mode 100644 packages/test/src/persistentSubscription/listPersistentSubscriptionsToStream.test.ts delete mode 100644 packages/test/src/persistentSubscription/replayParkedMessagesToAll.test.ts delete mode 100644 packages/test/src/persistentSubscription/replayParkedMessagesToStream.test.ts delete mode 100644 packages/test/src/persistentSubscription/restartPersistentSubscriptionSubsystem.test.ts delete mode 100644 packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll-filters.test.ts delete mode 100644 packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll.test.ts delete mode 100644 packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToStream.test.ts delete mode 100644 packages/test/src/persistentSubscription/updatePersistentSubscriptionToAll.test.ts delete mode 100644 packages/test/src/persistentSubscription/updatePersistentSubscriptionToStream.test.ts delete mode 100644 packages/test/src/plugins/__snapshots__/client-certificates.deprecated.test.ts.snap delete mode 100644 packages/test/src/plugins/__snapshots__/client-certificates.test.ts.snap delete mode 100644 packages/test/src/plugins/client-certificates.deprecated.test.ts delete mode 100644 packages/test/src/plugins/client-certificates.test.ts delete mode 100644 packages/test/src/projections/createProjection.test.ts delete mode 100644 packages/test/src/projections/deleteProjection.test.ts delete mode 100644 packages/test/src/projections/disableProjection.test.ts delete mode 100644 packages/test/src/projections/enableProjection.test.ts delete mode 100644 packages/test/src/projections/getProjectionResult.test.ts delete mode 100644 packages/test/src/projections/getProjectionState.test.ts delete mode 100644 packages/test/src/projections/getProjectionStatus.test.ts delete mode 100644 packages/test/src/projections/listProjections.test.ts delete mode 100644 packages/test/src/projections/resetProjection.test.ts delete mode 100644 packages/test/src/projections/restartSubsystem.test.ts delete mode 100644 packages/test/src/projections/updateProjection.test.ts delete mode 100644 packages/test/src/samples/appending-events.ts delete mode 100644 packages/test/src/samples/get-started.ts delete mode 100644 packages/test/src/samples/opentelemetry.ts delete mode 100644 packages/test/src/samples/persistent-subscriptions.ts delete mode 100644 packages/test/src/samples/projection-management.ts delete mode 100644 packages/test/src/samples/reading-events.ts delete mode 100644 packages/test/src/samples/server-side-filtering.ts delete mode 100644 packages/test/src/samples/subscribing-to-streams.ts delete mode 100644 packages/test/src/samples/user-certificates.ts delete mode 100644 packages/test/src/streams/appendToStream-batch-append-flood.test.ts delete mode 100644 packages/test/src/streams/appendToStream-batch-append.test.ts delete mode 100644 packages/test/src/streams/appendToStream-errors.test.ts delete mode 100644 packages/test/src/streams/appendToStream.test.ts delete mode 100644 packages/test/src/streams/deleteStream.test.ts delete mode 100644 packages/test/src/streams/getStreamMetadata.test.ts delete mode 100644 packages/test/src/streams/readAll.test.ts delete mode 100644 packages/test/src/streams/setStreamMetadata.test.ts delete mode 100644 packages/test/src/streams/subscribeToAll-filters.test.ts delete mode 100644 packages/test/src/streams/subscribeToAll.test.ts delete mode 100644 packages/test/src/streams/subscribeToStream.test.ts delete mode 100644 packages/test/src/streams/systemStreams.test.ts delete mode 100644 packages/test/src/streams/tombstoneStream.test.ts diff --git a/packages/db-client/src/Client/index.ts b/packages/db-client/src/Client/index.ts index a4616456..689b7eaf 100644 --- a/packages/db-client/src/Client/index.ts +++ b/packages/db-client/src/Client/index.ts @@ -252,7 +252,6 @@ export class Client { } return new Client( - rustClient, { discover, nodePreference: options.nodePreference, @@ -266,13 +265,13 @@ export class Client { connectionName: options.connectionName, }, channelCredentials, - options.defaultCredentials + options.defaultCredentials, + rustClient ); } if (options.hosts.length > 1) { return new Client( - rustClient, { endpoints: options.hosts, nodePreference: options.nodePreference, @@ -286,12 +285,12 @@ export class Client { connectionName: options.connectionName, }, channelCredentials, - options.defaultCredentials + options.defaultCredentials, + rustClient ); } return new Client( - rustClient, { endpoint: options.hosts[0], throwOnAppendFailure: options.throwOnAppendFailure, @@ -301,30 +300,30 @@ export class Client { connectionName: options.connectionName, }, channelCredentials, - options.defaultCredentials + options.defaultCredentials, + rustClient, ); } constructor( - rustClient: bridge.RustClient, connectionSettings: DNSClusterOptions, channelCredentials?: ChannelCredentialOptions, - defaultUserCredentials?: Credentials + defaultUserCredentials?: Credentials, + rustClient?: bridge.RustClient, ); constructor( - rustClient: bridge.RustClient, connectionSettings: GossipClusterOptions, channelCredentials?: ChannelCredentialOptions, - defaultUserCredentials?: Credentials + defaultUserCredentials?: Credentials, + rustClient?: bridge.RustClient, ); constructor( - rustClient: bridge.RustClient, connectionSettings: SingleNodeOptions, channelCredentials?: ChannelCredentialOptions, - defaultUserCredentials?: Credentials + defaultUserCredentials?: Credentials, + rustClient?: bridge.RustClient, ); constructor( - rustClient: bridge.RustClient, { throwOnAppendFailure = true, keepAliveInterval = 10_000, @@ -334,7 +333,8 @@ export class Client { ...connectionSettings }: ConnectionSettings, channelCredentials: ChannelCredentialOptions = { insecure: false }, - defaultUserCredentials?: Credentials + defaultUserCredentials?: Credentials, + rustClient?: bridge.RustClient, ) { if (keepAliveInterval < -1) { throw new Error( @@ -366,7 +366,6 @@ export class Client { ); } - this.#rustClient = rustClient; this.#throwOnAppendFailure = throwOnAppendFailure; this.#keepAliveInterval = keepAliveInterval; this.#keepAliveTimeout = keepAliveTimeout; @@ -377,6 +376,24 @@ export class Client { this.#connectionName = connectionName; this.#http = new HTTP(this, channelCredentials, defaultUserCredentials); + let newRustClient: bridge.RustClient | undefined = rustClient; + + if (newRustClient === undefined) { + if ("endpoint" in connectionSettings) { + newRustClient = bridge.createClient((this.#connectionSettings as SingleNodeOptions).endpoint.toString()); + } else if ("endpoints" in connectionSettings) { + newRustClient = bridge.createClient((this.#connectionSettings as GossipClusterOptions).endpoints.reduce( + (acc, chunk, i) => `${acc}${chunk}${(this.#connectionSettings as GossipClusterOptions).endpoints[i] ?? ""}`, + "")); + } else if ("discover" in connectionSettings) { + newRustClient = bridge.createClient((this.#connectionSettings as GossipClusterOptions).endpoints.reduce( + (acc, chunk, i) => `${acc}${chunk}${(this.#connectionSettings as GossipClusterOptions).endpoints[i] ?? ""}`, + "")); + } + } + + this.#rustClient = newRustClient!; + if (this.#insecure) { debug.connection("Using insecure channel"); this.#channelCredentials = grpcCredentials.createInsecure(); diff --git a/packages/test/src/connection/Channel.test.ts b/packages/test/src/connection/Channel.test.ts deleted file mode 100644 index 6619c702..00000000 --- a/packages/test/src/connection/Channel.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { collect, createTestCluster, jsonTestEvents } from "@test-utils"; -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("Channel", () => { - const cluster = createTestCluster(); - - beforeAll(async () => { - await cluster.up(); - }); - - afterAll(async () => { - await cluster.down(); - }); - - test("a single client should connect to a single node", async () => { - const client = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - nodePreference: "random", - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - /* - Spying on an internal api is more implementation specific than - I would like, but there is no easy way to check this. - */ - const discovery = jest.spyOn(client, "resolveUri" as never); - - const promises: Promise[] = [ - client.appendToStream("stream_1", jsonTestEvents()), - collect( - client.readAll({ - maxCount: 1, - fromPosition: "start", - direction: "forwards", - }) - ), - client.appendToStream("stream_2", jsonTestEvents()), - ]; - - await Promise.all(promises); - - expect(discovery).toBeCalledTimes(1); - }); -}); diff --git a/packages/test/src/connection/__snapshots__/keepAlive.test.ts.snap b/packages/test/src/connection/__snapshots__/keepAlive.test.ts.snap deleted file mode 100644 index a4bbaa53..00000000 --- a/packages/test/src/connection/__snapshots__/keepAlive.test.ts.snap +++ /dev/null @@ -1,89 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`keepAlive settings should throw if < -1 keepAliveInterval connectionString 1`] = `"Invalid keepAliveInterval \\"-2\\". Please provide a positive integer, or -1 to disable."`; - -exports[`keepAlive settings should throw if < -1 keepAliveInterval constructor 1`] = `"Invalid keepAliveInterval \\"-2\\". Please provide a positive integer, or -1 to disable."`; - -exports[`keepAlive settings should throw if < -1 keepAliveTimeout connectionString 1`] = `"Invalid keepAliveTimeout \\"-100\\". Please provide a positive integer, or -1 to disable."`; - -exports[`keepAlive settings should throw if < -1 keepAliveTimeout constructor 1`] = `"Invalid keepAliveTimeout \\"-100\\". Please provide a positive integer, or -1 to disable."`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 1`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 0 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 2`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 1 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 3`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 10 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 4`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 1000 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) connectionString 5`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 9999 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 1`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 0 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 2`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 1 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 3`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 10 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 4`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 1000 is less than recommended 10_000 ms.", - ], -] -`; - -exports[`keepAlive settings should warn if keepAliveInterval is less than 10_000ms (but more than -1) constructor 5`] = ` -Array [ - Array [ - "Specified KeepAliveInterval of 9999 is less than recommended 10_000 ms.", - ], -] -`; diff --git a/packages/test/src/connection/__snapshots__/parseConnectionString.test.ts.snap b/packages/test/src/connection/__snapshots__/parseConnectionString.test.ts.snap deleted file mode 100644 index a02da713..00000000 --- a/packages/test/src/connection/__snapshots__/parseConnectionString.test.ts.snap +++ /dev/null @@ -1,67 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`connection string parser Should throw on invalid strings esbd+discovery://localhost 1`] = `"Unexpected \\"esbd+discovery://\\" at position 0, expected esdb:// or esdb+discover://."`; - -exports[`connection string parser Should throw on invalid strings esdb://host1,host2:200:300?throwOnAppendFailure=false 1`] = `"Unexpected \\":300\\" at position 22, expected , or ?key=value."`; - -exports[`connection string parser Should throw on invalid strings esdb://host1;host2;host3?throwOnAppendFailure=false 1`] = `"Unexpected \\";\\" at position 12, expected ?key=value."`; - -exports[`connection string parser Should throw on invalid strings esdb://localhost/&throwOnAppendFailure=false 1`] = `"Unexpected \\"&\\" at position 17, expected ?key=value."`; - -exports[`connection string parser Should throw on invalid strings esdb://localhost?keepAliveInterval=XXIV 1`] = `"Unexpected \\"XXIV\\" at position 35, expected Integer."`; - -exports[`connection string parser Should throw on invalid strings esdb://localhost?keepAliveTimeout=please 1`] = `"Unexpected \\"please\\" at position 34, expected Integer."`; - -exports[`connection string parser Should throw on invalid strings esdb://localhost?throwOnAppendFailure=false&nodePreference=any 1`] = `"Unexpected \\"any\\" at position 59, expected leader or follower or read_only_replica or random."`; - -exports[`connection string parser Should throw on invalid strings esdb://localhost?throwOnAppendFailure=false?nodePreference=follower 1`] = `"Unexpected \\"?\\" at position 43, expected &key=value."`; - -exports[`connection string parser Should throw on invalid strings esdb://localhost?throwOnAppendFailure=if you feel like it 1`] = `"Unexpected \\"if you feel like it\\" at position 38, expected true or false."`; - -exports[`connection string parser Should throw on invalid strings esdb://localhost?throwOnAppendFailure=sometimes 1`] = `"Unexpected \\"sometimes\\" at position 38, expected true or false."`; - -exports[`connection string parser Should throw on invalid strings esdb://my:great@username:UyeXx8$^PsOo4jG88FlCauR1Coz25q@host?nodePreference=follower&throwOnAppendFailure=false 1`] = `"Unexpected \\"UyeXx8\\" at position 25, expected port number."`; - -exports[`connection string parser Should throw on invalid strings esdb://throwOnAppendFailure=false 1`] = `"Unexpected \\"=\\" at position 27, expected ?key=value."`; - -exports[`connection string parser Should throw on invalid strings https://console.eventstore.cloud/ 1`] = `"Unexpected \\"https://\\" at position 0, expected esdb:// or esdb+discover://."`; - -exports[`connection string parser Should throw on invalid strings localhost 1`] = `"Unexpected \\"l\\" at position 0, expected esdb:// or esdb+discover://."`; - -exports[`connection string parser Should warn on unknown and unsupported keys esdb://localhost?catchOnAppendFailure=true&throwOnAppendFailure=false 1`] = ` -Array [ - Array [ - "Unknown option key \\"catchOnAppendFailure\\", setting will be ignored. -esdb://localhost?catchOnAppendFailure=true&throwOnAppendFailure=false - ^^^^^^^^^^^^^^^^^^^^", - ], -] -`; - -exports[`connection string parser Should warn on unknown and unsupported keys esdb://localhost?someNonsense=follower&doTheThing=true 1`] = ` -Array [ - Array [ - "Unknown option key \\"someNonsense\\", setting will be ignored. -esdb://localhost?someNonsense=follower&doTheThing=true - ^^^^^^^^^^^^", - ], - Array [ - "Unknown option key \\"doTheThing\\", setting will be ignored. -esdb://localhost?someNonsense=follower&doTheThing=true - ^^^^^^^^^^", - ], -] -`; - -exports[`connection string parser Should warn on unknown and unsupported keys esdb://localhost?tlsVerifyCert=false 1`] = ` -Array [ - Array [ - "\\"tlsVerifyCert\\" is not currently supported by this client, and will have no effect. -Consider either: - Passing \\"tlsCAFile\\" in the connection string. - Setting NODE_EXTRA_CA_CERTS https://nodejs.org/api/cli.html#cli_node_extra_ca_certs_file -esdb://localhost?tlsVerifyCert=false - ^^^^^^^^^^^^^^^^^^^", - ], -] -`; diff --git a/packages/test/src/connection/__snapshots__/tlsCAFile.test.ts.snap b/packages/test/src/connection/__snapshots__/tlsCAFile.test.ts.snap deleted file mode 100644 index 3a9c5df7..00000000 --- a/packages/test/src/connection/__snapshots__/tlsCAFile.test.ts.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`tlsCAFile If a file was not found, error should be thrown 1`] = `"Failed to load certificate file. File was not found."`; diff --git a/packages/test/src/connection/cluster.test.ts b/packages/test/src/connection/cluster.test.ts deleted file mode 100644 index 6e599a98..00000000 --- a/packages/test/src/connection/cluster.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { collect, createTestCluster } from "@test-utils"; -import { jsonEvent, EventStoreDBClient } from "@eventstore/db-client"; - -describe("cluster", () => { - const cluster = createTestCluster(); - const STREAM_NAME = "test_stream_name"; - const event = jsonEvent({ type: "test", data: { message: "test" } }); - - beforeAll(async () => { - await cluster.up(); - }); - - afterAll(async () => { - await cluster.down(); - }); - - test("should successfully connect", async () => { - const client = new EventStoreDBClient( - { endpoints: cluster.endpoints }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - const appendResult = await client.appendToStream(STREAM_NAME, event); - const readResult = collect( - client.readStream(STREAM_NAME, { maxCount: 10 }) - ); - - expect(appendResult).toBeDefined(); - expect(readResult).toBeDefined(); - }); - - test("maxDiscoverAttempts", async () => { - const maxDiscoverAttempts = 3; - - const client = new EventStoreDBClient( - { - endpoints: [ - { address: "localhost", port: 8888 }, - { address: "localhost", port: 8889 }, - { address: "localhost", port: 8890 }, - ], - maxDiscoverAttempts, - }, - { rootCertificate: cluster.certs.root } - ); - - await expect( - client.appendToStream(STREAM_NAME, event) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to discover after ${maxDiscoverAttempts} attempts."` - ); - }); - - test("discoverInterval", async () => { - const maxDiscoverAttempts = 3; - const endpoints = [ - { address: "localhost", port: 8888 }, - { address: "localhost", port: 8889 }, - { address: "localhost", port: 8890 }, - ]; - - const client1DiscoveryInterval = 1; - const client2DiscoveryInterval = 5_000; - - const client1Start = Date.now(); - const client1 = new EventStoreDBClient({ - endpoints, - maxDiscoverAttempts, - discoveryInterval: client1DiscoveryInterval, - }); - await expect( - client1.appendToStream(STREAM_NAME, event) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to discover after ${maxDiscoverAttempts} attempts."` - ); - const client1Duration = Date.now() - client1Start; - - const client2Start = Date.now(); - const client2 = new EventStoreDBClient({ - endpoints, - maxDiscoverAttempts, - discoveryInterval: client2DiscoveryInterval, - }); - await expect( - client2.appendToStream(STREAM_NAME, event) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to discover after ${maxDiscoverAttempts} attempts."` - ); - const client2Duration = Date.now() - client2Start; - - expect(client2Duration).toBeGreaterThan(client1Duration); - expect(client2Duration).toBeGreaterThanOrEqual( - client2DiscoveryInterval * maxDiscoverAttempts - ); - - const expectedDifference = - client2DiscoveryInterval * maxDiscoverAttempts - - client1DiscoveryInterval * maxDiscoverAttempts; - - const actualDifference = client2Duration - client1Duration; - const discrepency = actualDifference - expectedDifference; - const slop = 2000; - - expect(discrepency).toBeLessThanOrEqual(slop); - expect(discrepency).toBeGreaterThanOrEqual(-slop); - }); -}); diff --git a/packages/test/src/connection/connectionName.test.ts b/packages/test/src/connection/connectionName.test.ts deleted file mode 100644 index d286bbc4..00000000 --- a/packages/test/src/connection/connectionName.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("connectionName", () => { - test("constructor", async () => { - const CONNECTION_NAME = "my great connection"; - const client = new EventStoreDBClient({ - endpoint: "somewhere", - connectionName: CONNECTION_NAME, - }); - - expect(client.connectionName).toBe(CONNECTION_NAME); - }); - - test("connection string", async () => { - const CONNECTION_NAME = "my great connection"; - const client = EventStoreDBClient.connectionString`esdb://host?connectionName=${CONNECTION_NAME}`; - - expect(client.connectionName).toBe(CONNECTION_NAME); - }); - - test("default", async () => { - const client = new EventStoreDBClient({ - endpoint: "somewhere", - }); - - expect(client.connectionName).toMatch( - // a (v4) uuid - /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/ - ); - }); -}); diff --git a/packages/test/src/connection/connectionString/connectionStringTests.ts b/packages/test/src/connection/connectionString/connectionStringTests.ts deleted file mode 100644 index 70d22175..00000000 --- a/packages/test/src/connection/connectionString/connectionStringTests.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Cluster, collect, delay, jsonTestEvents } from "@test-utils"; - -import { EventStoreDBClient } from "@eventstore/db-client"; - -interface ConnectionStringTestsOptions { - title: string; - createServer: () => Cluster; - createUri: (c: Cluster) => string; - createQueryString: (c: Cluster) => string; - streamPrefix: string; -} - -export const connectionStringTests = ({ - title, - createServer, - createUri, - createQueryString, - streamPrefix, -}: ConnectionStringTestsOptions) => - describe(`connectionString ${title}`, () => { - const server = createServer(); - - beforeAll(async () => { - await server.up(); - }); - - afterAll(async () => { - await server.down(); - }); - - test("template string", async () => { - const STREAM_NAME = `${streamPrefix}_template_string_stream`; - const uri = createUri(server); - const query = createQueryString(server); - const client = EventStoreDBClient.connectionString`esdb://admin:changeit@${uri}?${query}`; - - const appendResult = await client.appendToStream( - STREAM_NAME, - jsonTestEvents() - ); - await delay(100); - const readResult = await collect( - client.readStream(STREAM_NAME, { - maxCount: 10, - }) - ); - - expect(appendResult).toBeDefined(); - expect(readResult).toBeDefined(); - }); - - test("string argument", async () => { - const STREAM_NAME = `${streamPrefix}_string_stream`; - const uri = createUri(server); - const query = createQueryString(server); - const client = EventStoreDBClient.connectionString( - `esdb://admin:changeit@${uri}?${query}` - ); - - const appendResult = await client.appendToStream( - STREAM_NAME, - jsonTestEvents() - ); - await delay(100); - const readResult = await collect( - client.readStream(STREAM_NAME, { - maxCount: 10, - }) - ); - - expect(appendResult).toBeDefined(); - expect(readResult).toBeDefined(); - }); - - test("default credentials", async () => { - const uri = createUri(server); - const query = createQueryString(server); - const client = EventStoreDBClient.connectionString`esdb://admin:changeit@${uri}?${query}`; - await expect( - collect(client.readAll({ maxCount: 10 })) - ).resolves.toBeDefined(); - }); - }); diff --git a/packages/test/src/connection/connectionString/insecure-cluster.test.ts b/packages/test/src/connection/connectionString/insecure-cluster.test.ts deleted file mode 100644 index 7e2bc884..00000000 --- a/packages/test/src/connection/connectionString/insecure-cluster.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createInsecureTestCluster } from "@test-utils"; - -import { connectionStringTests } from "./connectionStringTests"; - -connectionStringTests({ - title: "Insecure Cluster", - createServer: createInsecureTestCluster, - createUri: ({ endpoints }) => - endpoints.map(({ address, port }) => `${address}:${port}`).join(","), - createQueryString: () => `tls=false`, - streamPrefix: "insecure-cluster", -}); diff --git a/packages/test/src/connection/connectionString/insecure-single-node.test.ts b/packages/test/src/connection/connectionString/insecure-single-node.test.ts deleted file mode 100644 index c6cde544..00000000 --- a/packages/test/src/connection/connectionString/insecure-single-node.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createInsecureTestNode } from "@test-utils"; - -import { connectionStringTests } from "./connectionStringTests"; - -connectionStringTests({ - title: "Insecure Single Node", - createServer: createInsecureTestNode, - createUri: ({ uri }) => uri, - createQueryString: () => `tls=false`, - streamPrefix: "insecure-single-node", -}); diff --git a/packages/test/src/connection/connectionString/secure-cluster.test.ts b/packages/test/src/connection/connectionString/secure-cluster.test.ts deleted file mode 100644 index 1074a210..00000000 --- a/packages/test/src/connection/connectionString/secure-cluster.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createTestCluster } from "@test-utils"; - -import { connectionStringTests } from "./connectionStringTests"; - -connectionStringTests({ - title: "Secure Cluster", - createServer: createTestCluster, - createUri: ({ endpoints }) => - endpoints.map(({ address, port }) => `${address}:${port}`).join(","), - createQueryString: ({ certPath }) => `tlsCAFile=${certPath.root}`, - streamPrefix: "secure-cluster", -}); diff --git a/packages/test/src/connection/connectionString/secure-single-node.test.ts b/packages/test/src/connection/connectionString/secure-single-node.test.ts deleted file mode 100644 index e47c1de6..00000000 --- a/packages/test/src/connection/connectionString/secure-single-node.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createTestNode } from "@test-utils"; - -import { connectionStringTests } from "./connectionStringTests"; - -connectionStringTests({ - title: "Secure Single Node", - createServer: createTestNode, - createUri: ({ uri }) => uri, - createQueryString: ({ certPath }) => `tlsCAFile=${certPath.root}`, - streamPrefix: "secure-single-node", -}); diff --git a/packages/test/src/connection/deadline/__snapshots__/deadline-settings.test.ts.snap b/packages/test/src/connection/deadline/__snapshots__/deadline-settings.test.ts.snap deleted file mode 100644 index 3ee29fe9..00000000 --- a/packages/test/src/connection/deadline/__snapshots__/deadline-settings.test.ts.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`deadline should throw on negative connectionString 1`] = `"Invalid defaultDeadline \\"-1\\". Please provide a positive integer."`; - -exports[`deadline should throw on negative connectionString 2`] = `"Invalid defaultDeadline \\"-1000000000000000\\". Please provide a positive integer."`; - -exports[`deadline should throw on negative constructor 1`] = `"Invalid defaultDeadline \\"-1\\". Please provide a positive integer."`; - -exports[`deadline should throw on negative constructor 2`] = `"Invalid defaultDeadline \\"-1000000000000000\\". Please provide a positive integer."`; - -exports[`deadline should throw on zero connectionString 1`] = `"Invalid defaultDeadline \\"0\\". Please provide a positive integer."`; - -exports[`deadline should throw on zero constructor 1`] = `"Invalid defaultDeadline \\"0\\". Please provide a positive integer."`; diff --git a/packages/test/src/connection/deadline/deadline-effects.test.ts b/packages/test/src/connection/deadline/deadline-effects.test.ts deleted file mode 100644 index ea46dae6..00000000 --- a/packages/test/src/connection/deadline/deadline-effects.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { createTestCluster, jsonTestEvents } from "@test-utils"; -import { - EventStoreDBClient, - DeadlineExceededError, -} from "@eventstore/db-client"; - -describe("deadline", () => { - const cluster = createTestCluster(); - - beforeAll(async () => { - await cluster.up(); - }); - - afterAll(async () => { - await cluster.down(); - }); - - describe("should time out a call", () => { - test.each([ - [ - "client settings", - () => - new EventStoreDBClient( - { endpoints: cluster.endpoints, defaultDeadline: 1 }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ).listProjections(), - ], - [ - "call options", - () => - new EventStoreDBClient( - { endpoints: cluster.endpoints }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ).listProjections({ - deadline: 1, - }), - ], - [ - "call options override", - () => - new EventStoreDBClient( - { endpoints: cluster.endpoints, defaultDeadline: 200_000 }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ).listProjections({ - deadline: 1, - }), - ], - [ - "append", - () => - new EventStoreDBClient( - { endpoints: cluster.endpoints, defaultDeadline: 200_000 }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ).appendToStream("deadline", jsonTestEvents(), { - deadline: 1, - }), - ], - ])("%s", async (_, makeCall) => { - try { - await makeCall(); - } catch (error) { - expect(error).toBeInstanceOf(DeadlineExceededError); - } - }); - }); -}); diff --git a/packages/test/src/connection/deadline/deadline-settings.test.ts b/packages/test/src/connection/deadline/deadline-settings.test.ts deleted file mode 100644 index 8a75d730..00000000 --- a/packages/test/src/connection/deadline/deadline-settings.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Channel } from "@grpc/grpc-js"; - -import { - BaseOptions, - DNSClusterOptions, - EventStoreDBClient, -} from "@eventstore/db-client"; - -/* -Mocking this file breaks grpc, but allows us to check the settings passed to grpc -These tests need to be kept seperate to any tests that need to actually make calls -*/ -jest.mock("@grpc/grpc-js/build/src/channel.js"); -const ChannelMock = Channel as jest.Mock; - -describe("deadline", () => { - beforeEach(() => { - ChannelMock.mockClear(); - }); - - describe.each< - [ - test_name: string, - connection_string: string, - constructor_options: Partial, - call_options: BaseOptions, - expected: number - ] - >([ - ["should default to 10_000", "esdb://host", {}, {}, 10_000], - [ - "should be settable: 1", - "esdb://host?defaultDeadline=1", - { defaultDeadline: 1 }, - {}, - 1, - ], - [ - "should be settable: 100000", - "esdb://host?defaultDeadline=100000", - { defaultDeadline: 10_0000 }, - {}, - 10_0000, - ], - [ - "passing in call options should override default", - "esdb://host", - {}, - { deadline: 10_0000 }, - 10_0000, - ], - [ - "passing in call options should override settings", - "esdb://host?defaultDeadline=100000", - { defaultDeadline: 10_0000 }, - { deadline: 10 }, - 10, - ], - ])("%s", (_, connectionString, constructorOptions, callOptions, expected) => { - test.each([ - [ - "connectionString", - () => EventStoreDBClient.connectionString(connectionString), - ], - [ - "constructor", - () => - new EventStoreDBClient({ - endpoint: "host:1234", - ...constructorOptions, - }), - ], - ])("%s", async (_, createClient) => { - const warnSpy = jest.spyOn(console, "warn").mockImplementation(); - const client = createClient(); - const before = Date.now(); - - try { - await client.restartSubsystem(callOptions); - } catch (_) { - // We're not actually connecting to anything, just triggering channel creation - } - - const after = Date.now(); - - const ChannelInstance = ChannelMock.mock.instances[0]; - const createCall = ChannelInstance.createCall as unknown as jest.Mock< - Channel["createCall"] - >; - - const deadline = createCall.mock.calls[0][1].getTime(); - - expect(deadline).toBeGreaterThanOrEqual(before + expected); - expect(deadline).toBeLessThanOrEqual(after + expected); - - expect(warnSpy).not.toBeCalled(); - warnSpy.mockRestore(); - }); - }); - - describe.each< - [ - test_name: string, - connection_string: string, - constructor_options: Partial - ] - >([ - [ - "should throw on zero", - "esdb://host?defaultDeadline=0", - { defaultDeadline: 0 }, - ], - [ - "should throw on negative", - "esdb://host?defaultDeadline=-1", - { defaultDeadline: -1 }, - ], - [ - "should throw on negative", - "esdb://host?defaultDeadline=-1000000000000000", - { defaultDeadline: -1000000000000000 }, - ], - ])("%s", (_, connectionString, constructorOptions) => { - test.each([ - [ - "connectionString", - () => EventStoreDBClient.connectionString(connectionString), - ], - [ - "constructor", - () => - new EventStoreDBClient({ - endpoint: "host:1234", - ...constructorOptions, - }), - ], - ])("%s", async (_, createClient) => { - expect(() => createClient()).toThrowErrorMatchingSnapshot(); - }); - }); -}); diff --git a/packages/test/src/connection/defaultCredentials.test.ts b/packages/test/src/connection/defaultCredentials.test.ts deleted file mode 100644 index 260d62e6..00000000 --- a/packages/test/src/connection/defaultCredentials.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { collect, createTestNode } from "@test-utils"; -import { EventStoreDBClient, AccessDeniedError } from "@eventstore/db-client"; - -describe("defaultCredentials", () => { - const node = createTestNode(); - - beforeAll(async () => { - await node.up(); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should set default credentials to be used by commands", () => { - test("bad override", async () => { - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - await expect( - collect(client.readAll({ maxCount: 10 })) - ).resolves.toBeDefined(); - await expect( - collect( - client.readAll({ - maxCount: 10, - credentials: { username: "AzureDiamond", password: "hunter2" }, - }) - ) - ).rejects.toThrowError(AccessDeniedError); - }); - - test("good override", async () => { - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "AzureDiamond", password: "hunter2" } - ); - await expect( - collect(client.readAll({ maxCount: 10 })) - ).rejects.toThrowError(AccessDeniedError); - await expect( - collect( - client.readAll({ - maxCount: 10, - credentials: { username: "admin", password: "changeit" }, - }) - ) - ).resolves.toBeDefined(); - }); - }); -}); diff --git a/packages/test/src/connection/determineBestNode.test.ts b/packages/test/src/connection/determineBestNode.test.ts deleted file mode 100644 index 23e077ad..00000000 --- a/packages/test/src/connection/determineBestNode.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { v4 as uuid } from "uuid"; - -import { - FOLLOWER, - LEADER, - RANDOM, - READ_ONLY_REPLICA, - VNodeState, -} from "@eventstore/db-client"; -import { - determineBestNode, - filterAndOrderMembers, - MemberInfo, -} from "@eventstore/db-client/dist/Client/discovery"; - -const member = ({ - isAlive = true, - state = VNodeState.UNKNOWN, - timeStamp = Date.now() - Math.floor(Math.random() * 1000), - httpEndpoint = { - address: "localhost", - port: Math.floor(Math.random() * 10000), - }, - instanceId = uuid(), -}: Partial): MemberInfo => ({ - isAlive, - state, - timeStamp, - httpEndpoint, - instanceId, -}); - -describe("determineBestNode", () => { - test("should exclude disallowed states", async () => { - const members: MemberInfo[] = [ - // not alive - member({ - isAlive: false, - state: VNodeState.FOLLOWER, - }), - // not good state - member({ - isAlive: true, - state: VNodeState.SHUTDOWN, - }), - // not good state - member({ - isAlive: true, - state: VNodeState.MANAGER, - }), - // good, should be chosen - member({ - isAlive: true, - state: VNodeState.LEADER, - }), - ]; - - const node = determineBestNode("random", members); - - expect(node).toBeDefined(); - expect(node).toEqual(members[3].httpEndpoint); - }); -}); - -describe("determineBestNode", () => { - test("should exclude disallowed states", async () => { - const members: MemberInfo[] = [ - // not alive - member({ - isAlive: false, - state: VNodeState.FOLLOWER, - }), - // not good state - member({ - isAlive: true, - state: VNodeState.SHUTDOWN, - }), - // not good state - member({ - isAlive: true, - state: VNodeState.MANAGER, - }), - // good, should be chosen - member({ - isAlive: true, - state: VNodeState.LEADER, - }), - ]; - - const node = determineBestNode("random", members); - - expect(node).toBeDefined(); - expect(node).toEqual(members[3].httpEndpoint); - }); -}); - -describe("member sorting", () => { - const members: MemberInfo[] = [ - member({ - state: VNodeState.FOLLOWER, - }), - member({ - state: VNodeState.FOLLOWER, - }), - member({ - state: VNodeState.READONLYREPLICA, - }), - member({ - state: VNodeState.LEADER, - }), - member({ - state: VNodeState.READONLYREPLICA, - }), - member({ - state: VNodeState.READONLYLEADERLESS, - }), - member({ - state: VNodeState.PREREADONLYREPLICA, - }), - ]; - - const countUnique = (sortedArrays: MemberInfo[][]): number => - new Set( - sortedArrays.map((arr) => arr.map(({ instanceId }) => instanceId).join()) - ).size; - - test(`RANDOM should shuffle the array`, () => { - const sortedArrays = Array.from({ length: 200 }, () => - filterAndOrderMembers(RANDOM, [...members]) - ); - - expect(countUnique(sortedArrays)).toBeGreaterThan(1); - }); - - test(`LEADER should place the leader first, shuffle the rest`, () => { - const sortedArrays = Array.from({ length: 200 }, () => - filterAndOrderMembers(LEADER, [...members]) - ); - - for (const sorted of sortedArrays) { - // the first item should always be the leader node - expect(sorted[0].state).toEqual(VNodeState.LEADER); - // the rest are random - } - - expect(countUnique(sortedArrays)).toBeGreaterThan(1); - }); - - test(`FOLLOWER should place the followers first in a random order, shuffle the rest`, () => { - const sortedArrays = Array.from({ length: 200 }, () => - filterAndOrderMembers(FOLLOWER, [...members]) - ); - - for (const sorted of sortedArrays) { - // the two follower nodes should come first - expect(sorted[0].state).toEqual(VNodeState.FOLLOWER); - expect(sorted[1].state).toEqual(VNodeState.FOLLOWER); - // the rest are random - } - expect(countUnique(sortedArrays)).toBeGreaterThan(1); - }); - - test(`READ_ONLY_REPLICA should prefer ReadOnlyReplica PreReadOnlyReplica ReadOnlyLeaderless in order`, () => { - const sortedArrays = Array.from({ length: 200 }, () => - filterAndOrderMembers(READ_ONLY_REPLICA, [...members]) - ); - - for (const sorted of sortedArrays) { - // the items should be placed in preference order - expect(sorted[0].state).toEqual(VNodeState.READONLYREPLICA); - expect(sorted[1].state).toEqual(VNodeState.READONLYREPLICA); - expect(sorted[2].state).toEqual(VNodeState.PREREADONLYREPLICA); - expect(sorted[3].state).toEqual(VNodeState.READONLYLEADERLESS); - // the rest are random - } - - expect(countUnique(sortedArrays)).toBeGreaterThan(1); - }); -}); diff --git a/packages/test/src/connection/dns.test.ts b/packages/test/src/connection/dns.test.ts deleted file mode 100644 index ce74e980..00000000 --- a/packages/test/src/connection/dns.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { collect, jsonTestEvents, optionalDescribe } from "@test-utils"; -import { - EventStoreDBClient, - jsonEvent, - NodePreference, - NotLeaderError, -} from "@eventstore/db-client"; - -optionalDescribe(!!process.env.EVENTSTORE_CLOUD_ID)("dns discover", () => { - const STREAM_NAME = "test_stream_name"; - const { EVENTSTORE_CLOUD_ID } = process.env; - const event = jsonEvent({ - type: "test", - data: { message: "test" }, - }); - - describe.each([ - [ - "connectionString", - (nodePreference?: NodePreference) => - EventStoreDBClient.connectionString`esdb+discover://${EVENTSTORE_CLOUD_ID!}.mesdb.eventstore.cloud${ - nodePreference ? `?nodePreference=${nodePreference}` : "" - }`, - ], - [ - "new client", - (nodePreference?: NodePreference) => - new EventStoreDBClient({ - discover: { - address: `${EVENTSTORE_CLOUD_ID!}.mesdb.eventstore.cloud`, - port: 2113, - }, - nodePreference, - }), - ], - ])("%s", (clientType, createClient) => { - test("should successfully connect", async () => { - const client = createClient(); - - const appendResult = await client.appendToStream(STREAM_NAME, event); - const readResult = await collect( - client.readStream(STREAM_NAME, { maxCount: 10 }) - ); - - expect(appendResult).toBeDefined(); - expect(readResult).toBeDefined(); - }); - - describe("should connect to specified preference", () => { - test("leader", async () => { - const client = createClient("leader"); - const appendResult = await client.appendToStream( - `${clientType}-leader-test`, - jsonTestEvents(), - { requiresLeader: true } - ); - - expect(appendResult).toBeDefined(); - }); - - test("follower", async () => { - const client = createClient("follower"); - - try { - const appendResult = await client.appendToStream( - `${clientType}-leader-test`, - jsonTestEvents(), - { requiresLeader: true } - ); - expect(appendResult).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(NotLeaderError); - } - }); - }); - }); -}); diff --git a/packages/test/src/connection/insecure.test.ts b/packages/test/src/connection/insecure.test.ts deleted file mode 100644 index 1b7c7592..00000000 --- a/packages/test/src/connection/insecure.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { collect, createInsecureTestNode } from "@test-utils"; -import { EventStoreDBClient, jsonEvent } from "@eventstore/db-client"; - -describe("insecure", () => { - const node = createInsecureTestNode(); - const STREAM_NAME = "test_stream_name"; - const event = jsonEvent({ type: "test", data: { message: "test" } }); - - beforeAll(async () => { - await node.up(); - }); - - afterAll(async () => { - await node.down(); - }); - - test("should successfully connect", async () => { - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { insecure: true } - ); - - const appendResult = await client.appendToStream(STREAM_NAME, event); - const readResult = await collect( - client.readStream(STREAM_NAME, { maxCount: 10 }) - ); - - expect(appendResult).toBeDefined(); - expect(readResult).toBeDefined(); - }); -}); diff --git a/packages/test/src/connection/keepAlive.test.ts b/packages/test/src/connection/keepAlive.test.ts deleted file mode 100644 index 5087b999..00000000 --- a/packages/test/src/connection/keepAlive.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { Channel } from "@grpc/grpc-js"; -import { EventStoreDBClient } from "@eventstore/db-client"; - -jest.mock("@grpc/grpc-js/build/src/channel.js"); -const ChannelMock = Channel as jest.Mock; - -describe("keepAlive settings", () => { - const endpoint = "host:1234"; - - beforeEach(() => { - ChannelMock.mockClear(); - }); - - describe("should both default to 10_000", () => { - describe.each([ - [ - "keepAliveInterval should default to 10_000", - "esdb://host", - {}, - { - "grpc.keepalive_time_ms": 10000, - }, - ], - [ - "keepAliveTimeout should default to 10_000", - "esdb://host", - {}, - { - "grpc.keepalive_timeout_ms": 10000, - }, - ], - [ - "keepAliveInterval should be settable (leaving timeout as default)", - "esdb://host?keepAliveInterval=123456", - { keepAliveInterval: 123456 }, - { - "grpc.keepalive_time_ms": 123456, - "grpc.keepalive_timeout_ms": 10000, - }, - ], - [ - "keepAliveTimeout should be settable (leaving interval as default)", - "esdb://host?keepAliveTimeout=246810", - { keepAliveTimeout: 246810 }, - { - "grpc.keepalive_time_ms": 10000, - "grpc.keepalive_timeout_ms": 246810, - }, - ], - [ - "both should be settable", - "esdb://host?keepAliveInterval=9987654&keepAliveTimeout=124816", - { keepAliveInterval: 9987654, keepAliveTimeout: 124816 }, - { - "grpc.keepalive_time_ms": 9987654, - "grpc.keepalive_timeout_ms": 124816, - }, - ], - [ - "-1 should disable (max_int) keepAliveInterval (leaving timeout as default)", - "esdb://host?keepAliveInterval=-1", - { keepAliveInterval: -1 }, - { - "grpc.keepalive_time_ms": Number.MAX_VALUE, - "grpc.keepalive_timeout_ms": 10000, - }, - ], - [ - "-1 should disable (max_int) keepAliveTimeout (leaving interval as default)", - "esdb://host?keepAliveTimeout=-1", - { keepAliveTimeout: -1 }, - { - "grpc.keepalive_time_ms": 10000, - "grpc.keepalive_timeout_ms": Number.MAX_VALUE, - }, - ], - [ - "-1 should disable (max_int) both", - "esdb://host?keepAliveTimeout=-1&keepAliveInterval=-1", - { keepAliveTimeout: -1, keepAliveInterval: -1 }, - { - "grpc.keepalive_time_ms": Number.MAX_VALUE, - "grpc.keepalive_timeout_ms": Number.MAX_VALUE, - }, - ], - [ - "0 is fine (but a terrible choice)", - "esdb://host?keepAliveTimeout=0&keepAliveInterval=0", - { keepAliveTimeout: 0, keepAliveInterval: 0 }, - { - "grpc.keepalive_time_ms": 0, - "grpc.keepalive_timeout_ms": 0, - }, - ], - ])("%s", (_, connectionString, constructorOptions, expected) => { - test.each([ - [ - "connectionString", - () => EventStoreDBClient.connectionString(connectionString), - ], - [ - "constructor", - () => - new EventStoreDBClient({ - endpoint, - ...constructorOptions, - }), - ], - ])("%s", async (_, createClient) => { - const warnSpy = jest.spyOn(console, "warn").mockImplementation(); - const client = createClient(); - - try { - await client.restartSubsystem(); - } catch (_) { - // We're not actually connecting to anything, just triggering channel creation - } - - expect(ChannelMock.mock.calls).toHaveLength(1); - const [[, , options]] = ChannelMock.mock.calls; - expect(options).toMatchObject(expected); - warnSpy.mockRestore(); - }); - }); - }); - - describe("should throw if < -1", () => { - describe.each` - option | value - ${"keepAliveTimeout"} | ${-100} - ${"keepAliveInterval"} | ${-2} - `("$option", ({ option, value }) => { - test.each([ - [ - "connectionString", - (option: string, value: number) => - EventStoreDBClient.connectionString`esdb://host?${option}=${value}`, - ], - [ - "constructor", - (option: string, value: number) => - new EventStoreDBClient({ endpoint, [option]: value }), - ], - ])("%s", (_, testCase) => { - expect(() => testCase(option, value)).toThrowErrorMatchingSnapshot(); - }); - }); - }); - - describe("should warn if keepAliveInterval is less than 10_000ms (but more than -1)", () => { - test.each([ - [ - "connectionString", - (value: number) => - EventStoreDBClient.connectionString`esdb://host?keepAliveInterval=${value}`, - ], - [ - "constructor", - (value: number) => - new EventStoreDBClient({ endpoint, keepAliveInterval: value }), - ], - ])("%s", (_, testCase) => { - for (const keepAliveInterval of [0, 1, 10, 1000, 9999]) { - const warnSpy = jest.spyOn(console, "warn").mockImplementation(); - testCase(keepAliveInterval); - expect(warnSpy).toHaveBeenCalled(); - expect(warnSpy.mock.calls).toMatchSnapshot(); - warnSpy.mockRestore(); - } - }); - }); -}); diff --git a/packages/test/src/connection/not-leader.test.ts b/packages/test/src/connection/not-leader.test.ts deleted file mode 100644 index c332f96e..00000000 --- a/packages/test/src/connection/not-leader.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { collect, createTestCluster } from "@test-utils"; -import { - jsonEvent, - FOLLOWER, - ErrorType, - NotLeaderError, - EventStoreDBClient, - BACKWARDS, - END, -} from "@eventstore/db-client"; - -describe("not-leader", () => { - const cluster = createTestCluster(); - const STREAM_NAME = "test_stream_name"; - const event = jsonEvent({ type: "test", data: { message: "test" } }); - - beforeAll(async () => { - await cluster.up(); - }); - - afterAll(async () => { - await cluster.down(); - }); - - test("should get an error here", async () => { - const followerClient = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - nodePreference: FOLLOWER, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - const appendResult = await followerClient.appendToStream( - STREAM_NAME, - event - ); - - expect(appendResult).toBeDefined(); - - const readFromTestStream = (client: EventStoreDBClient) => { - return collect( - client.readStream(STREAM_NAME, { - maxCount: 10, - direction: BACKWARDS, - fromRevision: END, - requiresLeader: true, - }) - ); - }; - - try { - const readResult = await readFromTestStream(followerClient); - - expect(readResult).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(NotLeaderError); - - if (error instanceof NotLeaderError) { - expect(error.type).toBe(ErrorType.NOT_LEADER); - expect(error.leader).toBeDefined(); - expect(cluster.endpoints).toContainEqual(error.leader); - - const leaderClient = new EventStoreDBClient( - { - endpoint: error.leader, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - const readResult = await readFromTestStream(leaderClient); - - expect(readResult).toBeDefined(); - } - } - }); -}); diff --git a/packages/test/src/connection/parseConnectionString.test.ts b/packages/test/src/connection/parseConnectionString.test.ts deleted file mode 100644 index 3ea318a5..00000000 --- a/packages/test/src/connection/parseConnectionString.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { parseConnectionString } from "@eventstore/db-client/dist/Client/parseConnectionString"; -import { valid, invalid, warning } from "./parseConnectionStringMockups"; - -describe("connection string parser", () => { - describe("Should parse valid connection strings", () => { - test.each(valid)("%s", (connectionString, expected) => { - const parsed = parseConnectionString(connectionString); - expect(parsed).toStrictEqual(expected); - }); - }); - - describe("Should throw on invalid strings", () => { - test.each(invalid)("%s", (connectionString) => { - expect(() => { - const parsed = parseConnectionString(connectionString); - console.log(parsed); - }).toThrowErrorMatchingSnapshot(); - }); - }); - - describe("Should warn on unknown and unsupported keys", () => { - test.each(warning)("%s", (connectionString, expected) => { - const warnSpy = jest.spyOn(console, "warn").mockImplementation(); - const parsed = parseConnectionString(connectionString); - expect(parsed).toStrictEqual(expected); - expect(warnSpy).toHaveBeenCalled(); - expect(warnSpy.mock.calls).toMatchSnapshot(); - warnSpy.mockRestore(); - }); - }); -}); diff --git a/packages/test/src/connection/parseConnectionStringMockups.ts b/packages/test/src/connection/parseConnectionStringMockups.ts deleted file mode 100644 index f996cee9..00000000 --- a/packages/test/src/connection/parseConnectionStringMockups.ts +++ /dev/null @@ -1,545 +0,0 @@ -import type { ConnectionOptions } from "@eventstore/db-client/dist/Client/parseConnectionString"; - -export const valid: Array< - [connectionString: string, expected: ConnectionOptions] -> = [ - [ - "esdb://localhost", - { - dnsDiscover: false, - hosts: [ - { - address: "localhost", - port: 2113, - }, - ], - }, - ], - [ - "esdb://localhost:2114", - { - dnsDiscover: false, - hosts: [ - { - address: "localhost", - port: 2114, - }, - ], - }, - ], - [ - "esdb://user:pass@localhost:2114", - { - dnsDiscover: false, - defaultCredentials: { - username: "user", - password: "pass", - }, - hosts: [ - { - address: "localhost", - port: 2114, - }, - ], - }, - ], - [ - "esdb://user:pass@localhost:2114/", - { - dnsDiscover: false, - defaultCredentials: { - username: "user", - password: "pass", - }, - hosts: [ - { - address: "localhost", - port: 2114, - }, - ], - }, - ], - [ - "esdb://user:pass@localhost:2114/?throwOnAppendFailure=false", - { - dnsDiscover: false, - throwOnAppendFailure: false, - defaultCredentials: { - username: "user", - password: "pass", - }, - hosts: [ - { - address: "localhost", - port: 2114, - }, - ], - }, - ], - [ - "esdb://user:pass@localhost:2114?tls=false", - { - dnsDiscover: false, - tls: false, - defaultCredentials: { - username: "user", - password: "pass", - }, - hosts: [ - { - address: "localhost", - port: 2114, - }, - ], - }, - ], - [ - "esdb://host1,host2,host3", - { - dnsDiscover: false, - hosts: [ - { - address: "host1", - port: 2113, - }, - { - address: "host2", - port: 2113, - }, - { - address: "host3", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host1:1234,host2:4321,host3:3231", - { - dnsDiscover: false, - hosts: [ - { - address: "host1", - port: 1234, - }, - { - address: "host2", - port: 4321, - }, - { - address: "host3", - port: 3231, - }, - ], - }, - ], - [ - "esdb://bubaqp2rh41uf5akmj0g-0.mesdb.eventstore.cloud:2113,bubaqp2rh41uf5akmj0g-1.mesdb.eventstore.cloud:2113,bubaqp2rh41uf5akmj0g-2.mesdb.eventstore.cloud:2113", - { - dnsDiscover: false, - hosts: [ - { - address: "bubaqp2rh41uf5akmj0g-0.mesdb.eventstore.cloud", - port: 2113, - }, - { - address: "bubaqp2rh41uf5akmj0g-1.mesdb.eventstore.cloud", - port: 2113, - }, - { - address: "bubaqp2rh41uf5akmj0g-2.mesdb.eventstore.cloud", - port: 2113, - }, - ], - }, - ], - [ - "esdb://user:pass@host1:1234,host2:4321,host3:3231?nodePreference=follower", - { - dnsDiscover: false, - nodePreference: "follower", - defaultCredentials: { - username: "user", - password: "pass", - }, - hosts: [ - { - address: "host1", - port: 1234, - }, - { - address: "host2", - port: 4321, - }, - { - address: "host3", - port: 3231, - }, - ], - }, - ], - [ - "esdb://host1,host2,host3?tls=false", - { - dnsDiscover: false, - tls: false, - hosts: [ - { - address: "host1", - port: 2113, - }, - { - address: "host2", - port: 2113, - }, - { - address: "host3", - port: 2113, - }, - ], - }, - ], - [ - "esdb://127.0.0.1:21573?tls=false", - { - dnsDiscover: false, - tls: false, - hosts: [ - { - address: "127.0.0.1", - port: 21573, - }, - ], - }, - ], - [ - "esdb://host1,host2,host3?throwOnAppendFailure=false", - { - dnsDiscover: false, - throwOnAppendFailure: false, - hosts: [ - { - address: "host1", - port: 2113, - }, - { - address: "host2", - port: 2113, - }, - { - address: "host3", - port: 2113, - }, - ], - }, - ], - [ - "esdb+discover://user:pass@host?nodePreference=follower&throwOnAppendFailure=false", - { - dnsDiscover: true, - nodePreference: "follower", - throwOnAppendFailure: false, - defaultCredentials: { - username: "user", - password: "pass", - }, - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb+discover://host?nodePreference=Follower", - { - dnsDiscover: true, - nodePreference: "follower", - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?tlsCAFile=/home/user/dev/cert.ca", - { - dnsDiscover: false, - tlsCAFile: "/home/user/dev/cert.ca", - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?tlsCAFile=./cert.ca", - { - dnsDiscover: false, - tlsCAFile: "./cert.ca", - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?tlsCAFile=C:\\Certificates\\EventStore\\Cert.ca", - { - dnsDiscover: false, - tlsCAFile: "C:\\Certificates\\EventStore\\Cert.ca", - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?tlsCAFile=..\\EventStore\\Cert.ca", - { - dnsDiscover: false, - tlsCAFile: "..\\EventStore\\Cert.ca", - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?keepAliveInterval=-1&keepAliveTimeout=-1", - { - dnsDiscover: false, - keepAliveInterval: -1, - keepAliveTimeout: -1, - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://my%3Agreat%40username:UyeXx8%24%5EPsOo4jG88FlCauR1Coz25q@host?nodePreference=follower&throwOnAppendFailure=false&connectionName=wh%40t%3F%3A%26", - { - dnsDiscover: false, - nodePreference: "follower", - throwOnAppendFailure: false, - connectionName: "wh@t?:&", - defaultCredentials: { - username: "my:great@username", - password: "UyeXx8$^PsOo4jG88FlCauR1Coz25q", - }, - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb+discover://user:pass@морда-кошки.ru,ощущение-картофеля.ru?nodePreference=follower&throwOnAppendFailure=false&connectionName=соединение", - { - dnsDiscover: true, - nodePreference: "follower", - throwOnAppendFailure: false, - connectionName: "соединение", - defaultCredentials: { - username: "user", - password: "pass", - }, - hosts: [ - { - address: "морда-кошки.ru", - port: 2113, - }, - { - address: "ощущение-картофеля.ru", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?maxDiscoverAttempts=200&discoveryInterval=1000&gossipTimeout=1&nodePreference=leader&tls=false&tlsVerifyCert=true&throwOnAppendFailure=false&keepAliveInterval=10&defaultDeadline=10000000", - { - dnsDiscover: false, - maxDiscoverAttempts: 200, - discoveryInterval: 1000, - defaultDeadline: 10_000_000, - gossipTimeout: 1, - nodePreference: "leader", - tls: false, - tlsVerifyCert: true, - throwOnAppendFailure: false, - keepAliveInterval: 10, - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?MaxDiscoverAttempts=200&discoveryinterval=1000&GOSSIPTIMEOUT=1&nOdEpReFeReNcE=leader&TLS=false&TlsVerifyCert=true&THROWOnAppendFailure=false&KEEPALIVEinterval=200&CoNnEcTionNAME=wow, what a great connection", - { - dnsDiscover: false, - maxDiscoverAttempts: 200, - discoveryInterval: 1000, - gossipTimeout: 1, - nodePreference: "leader", - tls: false, - tlsVerifyCert: true, - connectionName: "wow, what a great connection", - throwOnAppendFailure: false, - keepAliveInterval: 200, - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - `esdb://host?MaxDiscoverAttempts=200&discovery-interval=1000&GOSSIP_TIMEOUT=1&node_preference=ReadOnlyReplica&TLS=false&TlsVerifyCert=true&DEFAULTdEADLINE=12&THROWOnAppendFailure=false & KEEPALIVEinterval=200`, - { - dnsDiscover: false, - maxDiscoverAttempts: 200, - discoveryInterval: 1000, - defaultDeadline: 12, - gossipTimeout: 1, - nodePreference: "read_only_replica", - tls: false, - tlsVerifyCert: true, - throwOnAppendFailure: false, - keepAliveInterval: 200, - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - `esdb:// host - ? MaxDiscoverAttempts=200 - &discoveryinterval=1000 - &GOSSIPTIMEOUT=1 - &nOdEpReFeReNcE=leader - &TLS=false - &TlsVerifyCert=true - & CONNECTIONname = my great - connection - & - THROWOnAppendFailure - = - false & KEEPALIVEinterval=200`, - { - dnsDiscover: false, - maxDiscoverAttempts: 200, - discoveryInterval: 1000, - gossipTimeout: 1, - nodePreference: "leader", - tls: false, - tlsVerifyCert: true, - connectionName: "my great \n connection", - throwOnAppendFailure: false, - keepAliveInterval: 200, - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], - [ - "esdb://host?userCertFile=/home/user/dev/cert.ca&userKeyFile=/home/user/dev/cert.key", - { - dnsDiscover: false, - userCertFile: "/home/user/dev/cert.ca", - userKeyFile: "/home/user/dev/cert.key", - hosts: [ - { - address: "host", - port: 2113, - }, - ], - }, - ], -]; - -export const invalid: string[] = [ - "localhost", - "https://console.eventstore.cloud/", - "esbd+discovery://localhost", - "esdb://my:great@username:UyeXx8$^PsOo4jG88FlCauR1Coz25q@host?nodePreference=follower&throwOnAppendFailure=false", - "esdb://host1;host2;host3?throwOnAppendFailure=false", - "esdb://host1,host2:200:300?throwOnAppendFailure=false", - "esdb://throwOnAppendFailure=false", - "esdb://localhost/&throwOnAppendFailure=false", - "esdb://localhost?throwOnAppendFailure=false?nodePreference=follower", - "esdb://localhost?throwOnAppendFailure=false&nodePreference=any", - "esdb://localhost?throwOnAppendFailure=if you feel like it", - "esdb://localhost?throwOnAppendFailure=sometimes", - "esdb://localhost?keepAliveTimeout=please", - "esdb://localhost?keepAliveInterval=XXIV", -]; - -export const warning: Array< - [connectionString: string, expected: ConnectionOptions] -> = [ - [ - "esdb://localhost?catchOnAppendFailure=true&throwOnAppendFailure=false", - { - dnsDiscover: false, - throwOnAppendFailure: false, - hosts: [ - { - address: "localhost", - port: 2113, - }, - ], - }, - ], - [ - "esdb://localhost?someNonsense=follower&doTheThing=true", - { - dnsDiscover: false, - hosts: [ - { - address: "localhost", - port: 2113, - }, - ], - }, - ], - [ - "esdb://localhost?tlsVerifyCert=false", - { - dnsDiscover: false, - tlsVerifyCert: false, - hosts: [ - { - address: "localhost", - port: 2113, - }, - ], - }, - ], -]; diff --git a/packages/test/src/connection/reconnect/NotLeaderError.test.ts b/packages/test/src/connection/reconnect/NotLeaderError.test.ts deleted file mode 100644 index 91a3a21b..00000000 --- a/packages/test/src/connection/reconnect/NotLeaderError.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { createTestCluster, getCurrentConnection } from "@test-utils"; -import { - jsonEvent, - EventStoreDBClient, - NotLeaderError, - FOLLOWER, - EndPoint, -} from "@eventstore/db-client"; - -// This test can take time. -jest.setTimeout(120_000); - -describe("reconnect", () => { - test("NotLeaderError (should reconnect to leader)", async () => { - const cluster = createTestCluster(); - - await cluster.up(); - - const client = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - nodePreference: FOLLOWER, - // The timing of this test can be a bit variable, - // so it's better not to have deadlines here to force the errors we are testing. - defaultDeadline: Infinity, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - // make successful append to follower node - const firstAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "first-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(firstAppend).toBeDefined(); - - let leader!: EndPoint; - try { - // state that append requires leader, causing failure - const secondAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "failed-append", data: { message: "test" } }), - { - requiresLeader: true, - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - credentials: { username: "admin", password: "changeit" }, - } - ); - expect(secondAppend).toBe("Unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(NotLeaderError); - - if (error instanceof NotLeaderError) { - leader = error.leader; - } - } - - // next append should succeed, as it has connected to another node - const reconnectedAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "reconnect-append", data: { message: "test" } }), - { - requiresLeader: true, - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - credentials: { username: "admin", password: "changeit" }, - } - ); - expect(reconnectedAppend).toBeDefined(); - - const { address, port } = await getCurrentConnection(client); - - expect(address).toEqual(leader.address); - expect(port).toEqual(port); - - await cluster.down(); - }); -}); diff --git a/packages/test/src/connection/reconnect/UnavailableError.test.ts b/packages/test/src/connection/reconnect/UnavailableError.test.ts deleted file mode 100644 index 496b9d76..00000000 --- a/packages/test/src/connection/reconnect/UnavailableError.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { createTestCluster, delay, getCurrentConnection } from "@test-utils"; -import { - jsonEvent, - EventStoreDBClient, - UnavailableError, -} from "@eventstore/db-client"; - -import { setLogger, setLogVerbosity, logVerbosity } from "@grpc/grpc-js"; - -setLogVerbosity(logVerbosity.DEBUG); - -setLogger(console); - -// This test can take time. -jest.setTimeout(120_000); - -describe("reconnect", () => { - test("UnavailableError", async () => { - const cluster = createTestCluster(); - - await cluster.up(); - - const client = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - // The timing of this test can be a bit variable, - // so it's better not to have deadlines here to force the errors we are testing. - defaultDeadline: Infinity, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - // make successful append to connect to node - const firstAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "first-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(firstAppend).toBeDefined(); - - // Kill node we are connected to - const activeConnection = await getCurrentConnection(client); - await cluster.killNode(activeConnection); - - // next append should fail, triggering reconnection - try { - const secondAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "failed-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(secondAppend).toBe("Unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(UnavailableError); - } - - // wait for leader to be ready - await delay(5000); - - const reconnectedAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "reconnect-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(reconnectedAppend).toBeDefined(); - - await cluster.down(); - }); -}); diff --git a/packages/test/src/connection/reconnect/all-nodes-down.test.ts b/packages/test/src/connection/reconnect/all-nodes-down.test.ts deleted file mode 100644 index 89c9df1f..00000000 --- a/packages/test/src/connection/reconnect/all-nodes-down.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { collect, createTestCluster, delay } from "@test-utils"; -import { - jsonEvent, - EventStoreDBClient, - UnavailableError, - persistentSubscriptionToStreamSettingsFromDefaults, - StreamNotFoundError, -} from "@eventstore/db-client"; - -// This test can take time. -jest.setTimeout(120_000); - -const STREAM_NAME = "my_stream"; - -describe("reconnect", () => { - const cluster = createTestCluster(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await cluster.up(); - - client = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - // The timing of this test can be a bit variable, - // so it's better not to have deadlines here to force the errors we are testing. - defaultDeadline: Infinity, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await cluster.down(); - }); - - test("All nodes down", async () => { - // make successful append to connect to node - const firstAppend = await client.appendToStream( - STREAM_NAME, - jsonEvent({ type: "first-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(firstAppend).toBeDefined(); - - // read the stream successfully - const firstReadStream = await collect( - client.readStream(STREAM_NAME, { maxCount: 10 }) - ); - expect(firstReadStream.length).toBe(1); - const firstEvent = firstReadStream[0].event; - expect(firstEvent?.data).toStrictEqual({ message: "test" }); - expect(firstEvent?.type).toBe("first-append"); - - // make successfull subscription to stream - const firstCreateSubscription = - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - "first-test-group", - persistentSubscriptionToStreamSettingsFromDefaults() - ); - expect(firstCreateSubscription).toBeUndefined(); - - // delete the stream successfully - const firstDeleteStream = await client.deleteStream(STREAM_NAME); - expect(firstDeleteStream).toBeDefined(); - await expect( - collect(client.readStream(STREAM_NAME, { maxCount: 10 })) - ).rejects.toThrowError(StreamNotFoundError); - - // Kill all nodes - for (const endpoint of cluster.endpoints) { - await cluster.killNode(endpoint); - } - - // next client operations should fail - - // append to stream - await expect( - client.appendToStream( - STREAM_NAME, - jsonEvent({ type: "failed-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ) - ).rejects.toThrowError(UnavailableError); - // read the stream - await expect(async () => { - let count = 0; - for await (const e of client.readStream(STREAM_NAME, { maxCount: 10 })) { - count++; - } - }).rejects.toThrowErrorMatchingInlineSnapshot( - '"Failed to discover after 10 attempts."' - ); - // create subsctiption - await expect( - client.createPersistentSubscriptionToStream( - STREAM_NAME, - "second-test-group", - persistentSubscriptionToStreamSettingsFromDefaults() - ) - ).rejects.toThrowErrorMatchingInlineSnapshot( - '"Failed to discover after 10 attempts."' - ); - // delete stream - await expect( - client.deleteStream(STREAM_NAME) - ).rejects.toThrowErrorMatchingInlineSnapshot( - '"Failed to discover after 10 attempts."' - ); - - // next operations should also fail, as there is nothing to reconnect to reconnection fail - // append to stream - await expect( - client.appendToStream( - STREAM_NAME, - jsonEvent({ type: "failed-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ) - ).rejects.toThrowErrorMatchingInlineSnapshot( - '"Failed to discover after 10 attempts."' - ); - // read the stream - await expect(async () => { - let count = 0; - for await (const e of client.readStream(STREAM_NAME, { maxCount: 10 })) { - count++; - } - }).rejects.toThrowErrorMatchingInlineSnapshot( - '"Failed to discover after 10 attempts."' - ); - // create subsctiption - await expect( - client.createPersistentSubscriptionToStream( - STREAM_NAME, - "third-test-group", - persistentSubscriptionToStreamSettingsFromDefaults() - ) - ).rejects.toThrowErrorMatchingInlineSnapshot( - '"Failed to discover after 10 attempts."' - ); - // delete stream - await expect( - client.deleteStream(STREAM_NAME) - ).rejects.toThrowErrorMatchingInlineSnapshot( - '"Failed to discover after 10 attempts."' - ); - - // resurrect all nodes - await cluster.resurrect(); - - // wait for leader to be ready - await delay(5000); - - const reconnectedAppend = await client.appendToStream( - STREAM_NAME, - jsonEvent({ type: "reconnect-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(reconnectedAppend).toBeDefined(); - - const reconnectReadStream = await collect( - client.readStream(STREAM_NAME, { maxCount: 10 }) - ); - expect(reconnectReadStream.length).toBe(1); - const reconnectEvent = reconnectReadStream[0].event; - expect(reconnectEvent?.data).toStrictEqual({ message: "test" }); - expect(reconnectEvent?.type).toBe("reconnect-append"); - - const reconndectedCreateSubscription = - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - "fourth-test-group", - persistentSubscriptionToStreamSettingsFromDefaults() - ); - expect(reconndectedCreateSubscription).toBeUndefined(); - - const reconnectedDeleteStream = await client.deleteStream(STREAM_NAME); - expect(reconnectedDeleteStream).toBeDefined(); - await expect( - collect(client.readStream(STREAM_NAME, { maxCount: 10 })) - ).rejects.toThrowError(StreamNotFoundError); - }); -}); diff --git a/packages/test/src/connection/reconnect/mid-stream.test.ts b/packages/test/src/connection/reconnect/mid-stream.test.ts deleted file mode 100644 index e8be5d44..00000000 --- a/packages/test/src/connection/reconnect/mid-stream.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestCluster, - delay, - getCurrentConnection, - jsonTestEvents, -} from "@test-utils"; -import { - jsonEvent, - EventStoreDBClient, - CancelledError, -} from "@eventstore/db-client"; - -// This test can take time. -jest.setTimeout(120_000); - -describe("reconnect", () => { - test("Connection error mid stream should cause a reconnect", async () => { - const cluster = createTestCluster(); - - await cluster.up(); - - const client = new EventStoreDBClient( - { endpoints: cluster.endpoints }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - // make successful append of 2000 events to node - const firstAppend = await client.appendToStream( - "my_stream", - jsonTestEvents(2000), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(firstAppend).toBeDefined(); - - try { - let i = 0; - for await (const event of client.readStream("my_stream")) { - expect(event).toBeDefined(); - - if (i === 12) { - // Kill node we are connected to - const activeConnection = await getCurrentConnection(client); - await cluster.killNode(activeConnection); - } - - i++; - } - - expect(i).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(CancelledError); - } - - // wait for leader to be ready - await delay(5000); - - const reconnectedAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "reconnect-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(reconnectedAppend).toBeDefined(); - - await cluster.down(); - }); -}); diff --git a/packages/test/src/connection/reconnect/no-reconnection.test.ts b/packages/test/src/connection/reconnect/no-reconnection.test.ts deleted file mode 100644 index 1d66fcd4..00000000 --- a/packages/test/src/connection/reconnect/no-reconnection.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { createTestCluster, createTestNode, jsonTestEvents } from "@test-utils"; -import { - jsonEvent, - EventStoreDBClient, - StreamNotFoundError, - TimeoutError, - WrongExpectedVersionError, -} from "@eventstore/db-client"; - -// This test can take time. -jest.setTimeout(120_000); - -describe("reconnect", () => { - test("no reconnection is made if error is not for reconnecting", async () => { - const cluster = createTestCluster(); - - await cluster.up(); - - const client = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - // The timing of this test can be a bit variable, - // so it's better not to have deadlines here to force the errors we are testing. - defaultDeadline: Infinity, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - // make successful append to connect to node - const firstAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "first-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials: { username: "admin", password: "changeit" } } - ); - expect(firstAppend).toBeDefined(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const priorChannel = await (client as any).getChannel(); - - // attempt to read a stream that doesn't exist should fail - await expect(async () => { - for await (const event of client.readStream("doesn't-exist")) { - expect(event).toBe("unreachable"); - } - }).rejects.toThrowError(StreamNotFoundError); - - // attempt to delete a stream that doesnt exist should fail - await expect(client.deleteStream("doesn't-exist")).rejects.toThrowError( - WrongExpectedVersionError - ); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const afterChannel = await (client as any).getChannel(); - - expect(priorChannel).toBe(afterChannel); - - await cluster.down(); - }); - - test("no reconnection for TimeoutError", async () => { - const timeoutNode = createTestNode() - .setOption("EVENTSTORE_COMMIT_TIMEOUT_MS", 1) - .setOption("EVENTSTORE_PREPARE_TIMEOUT_MS", 1); - - await timeoutNode.up(); - - const credentials = { username: "admin", password: "changeit" }; - const STREAM_NAME = "try_get_timeout"; - - const client = new EventStoreDBClient( - { - endpoint: timeoutNode.uri, - // The timing of this test can be a bit variable, - // so it's better not to have deadlines here to force the errors we are testing. - defaultDeadline: Infinity, - }, - { rootCertificate: timeoutNode.certs.root }, - { username: "admin", password: "changeit" } - ); - - // make successful append to connect to node - const firstAppend = await client.appendToStream( - "my_stream", - jsonEvent({ type: "first-append", data: { message: "test" } }), - // batch append triggers reconnect as soon as stream drops, so we need to force regular append - { credentials } - ); - expect(firstAppend).toBeDefined(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const priorChannel = await (client as any).getChannel(); - - try { - // try increasingly hard to hit the timeout - for (let i = 5; i < 20; i += 5) { - await Promise.all( - Array.from({ length: i }, () => - client.appendToStream(STREAM_NAME, jsonTestEvents(30_000), { - credentials, - }) - ) - ); - } - - expect("this point").toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(TimeoutError); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const afterChannel = await (client as any).getChannel(); - - expect(priorChannel).toBe(afterChannel); - - await timeoutNode.down(); - }); -}); diff --git a/packages/test/src/connection/singleNode.test.ts b/packages/test/src/connection/singleNode.test.ts deleted file mode 100644 index ae9b95b4..00000000 --- a/packages/test/src/connection/singleNode.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { collect, createTestNode } from "@test-utils"; -import { EventStoreDBClient, jsonEvent } from "@eventstore/db-client"; - -describe("singleNodeConnection", () => { - const node = createTestNode(); - const STREAM_NAME = "test_stream_name"; - const event = jsonEvent({ type: "test", data: { message: "test" } }); - - beforeAll(async () => { - await node.up(); - }); - - afterAll(async () => { - await node.down(); - }); - - test("should successfully connect", async () => { - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const appendResult = await client.appendToStream(STREAM_NAME, event); - const readResult = await collect( - client.readStream(STREAM_NAME, { maxCount: 10 }) - ); - - expect(appendResult).toBeDefined(); - expect(readResult).toBeDefined(); - }); -}); diff --git a/packages/test/src/connection/tlsCAFile.test.ts b/packages/test/src/connection/tlsCAFile.test.ts deleted file mode 100644 index 8aa1df8e..00000000 --- a/packages/test/src/connection/tlsCAFile.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { relative, resolve } from "path"; - -import { collect, createTestNode, jsonTestEvents } from "@test-utils"; -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("tlsCAFile", () => { - const node = createTestNode(); - - beforeAll(async () => { - await node.up(); - }); - - afterAll(async () => { - await node.down(); - }); - - test.each([ - ["relative", () => relative(process.cwd(), node.certPath.root)], - [ - "absolute", - // in case node.certPath.root implementation changes to be relative, make certain it is absolute - () => resolve(process.cwd(), relative(process.cwd(), node.certPath.root)), - ], - ])("Path can be %s", async (name, tlsCAFile) => { - const STREAM_NAME = `${name}_stream`; - - const client = EventStoreDBClient.connectionString`esdb://admin:changeit@${ - node.uri - }?tlsCAFile=${tlsCAFile()}`; - - const appendResult = await client.appendToStream( - STREAM_NAME, - jsonTestEvents() - ); - const readResult = await collect( - client.readStream(STREAM_NAME, { maxCount: 10 }) - ); - - expect(appendResult).toBeDefined(); - expect(readResult).toBeDefined(); - }); - - test("If a file was not found, error should be thrown", () => { - expect( - () => - EventStoreDBClient.connectionString`esdb://admin:changeit@${node.uri}?tlsCAFile=/some/path.ca` - ).toThrowErrorMatchingSnapshot(); - }); -}); diff --git a/packages/test/src/extra/RecordedEvent-created.test.ts b/packages/test/src/extra/RecordedEvent-created.test.ts deleted file mode 100644 index bbb7af24..00000000 --- a/packages/test/src/extra/RecordedEvent-created.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { createTestNode, jsonTestEvents, delay } from "@test-utils"; -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("RecordedEvent created", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("Should be a Date", async () => { - const STREAM_NAME = "test_stream_name"; - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - for await (const { event } of client.readStream(STREAM_NAME)) { - expect(event).toBeDefined; - expect(event?.created).toBeInstanceOf(Date); - } - }); - - test("Should correctly converted from Ticks", async () => { - const STREAM_NAME = "correct_conversion"; - - // The db / test is running on the same box, so we can assume that the time lines up - const before = Date.now(); - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - const after = Date.now(); - - // Lets wait 5 seconds before reading - await delay(5_000); - - for await (const { event } of client.readStream(STREAM_NAME)) { - expect(event?.created.valueOf()).toBeGreaterThanOrEqual(before); - expect(event?.created.valueOf()).toBeLessThanOrEqual(after); - } - }); -}); diff --git a/packages/test/src/extra/dispose.test.ts b/packages/test/src/extra/dispose.test.ts deleted file mode 100644 index fc65cad3..00000000 --- a/packages/test/src/extra/dispose.test.ts +++ /dev/null @@ -1,395 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import type { Stream } from "stream"; -import { v4 as uuid } from "uuid"; - -import { - createTestNode, - Defer, - delay, - jsonTestEvents, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; -import { - EventStoreDBClient, - persistentSubscriptionToAllSettingsFromDefaults, - persistentSubscriptionToStreamSettingsFromDefaults, - START, - streamNameFilter, -} from "@eventstore/db-client"; - -describe("dispose", () => { - const supported = matchServerVersion`>=21.10`; - - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - afterEach(async () => { - await client.dispose(); - }); - - test("stream set should be cleaned up", async () => { - const STREAM_NAME = uuid(); - const STREAM_NAME_2 = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(100), { - // We dont want to use a stream for this, so we can use the same number regardless of server support. - credentials: { username: "admin", password: "changeit" }, - }); - - const handleError = jest.fn(); - const handleEvent = jest.fn(); - - // 1 - client - .readStream(STREAM_NAME) - .on("error", handleError) - .on("data", handleEvent); - - // 2 - client - .readStream(STREAM_NAME_2) - .on("error", handleError) - .on("data", handleEvent); - - // 3 - client.readAll().on("error", handleError).on("data", handleEvent); - - // 4 - client - .subscribeToStream(STREAM_NAME) - .on("error", handleError) - .on("data", handleEvent); - - // 5 - client.subscribeToAll().on("error", handleError).on("data", handleEvent); - - // wait for next tick - await delay(0); - - expect(extractKnownStreams.call(client).size).toBe(5); - - await client.dispose(); - - expect(extractKnownStreams.call(client).size).toBe(0); - expect(handleError).not.toBeCalled(); - }); - - test("stream set should be cleaned up naturally", async () => { - const STREAM_NAME = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(10), { - // We dont want to use a stream for this, so we can use the same number regardless of server support. - credentials: { username: "admin", password: "changeit" }, - }); - - for await (const event of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(extractKnownStreams.call(client).size).toBe(1); - expect(event).toBeDefined(); - } - - expect(extractKnownStreams.call(client).size).toBe(0); - }); - - test("Subscription to stream", async () => { - const defer = new Defer(); - const STREAM_NAME = uuid(); - const STREAM_NAME_2 = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(); - const handleSubscription1End = jest.fn(defer.resolve); - const handleSubscription2End = jest.fn(defer.resolve); - - client - .subscribeToStream(STREAM_NAME) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handleSubscription1End); - - client - .subscribeToStream(STREAM_NAME_2) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handleSubscription2End); - - await delay(500); - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - await client.dispose(); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handleSubscription1End).toBeCalledTimes(1); - expect(handleSubscription2End).toBeCalledTimes(1); - }); - - test("Subscription to $all", async () => { - const defer = new Defer(); - const STREAM_NAME = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(); - const handleSubscription1End = jest.fn(defer.resolve); - const handleSubscription2End = jest.fn(defer.resolve); - - client - .subscribeToAll() - .on("error", handleError) - .on("data", handleEvent) - .on("end", handleSubscription1End); - - client - .subscribeToAll() - .on("error", handleError) - .on("data", handleEvent) - .on("end", handleSubscription2End); - - await delay(500); - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - await client.dispose(); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handleSubscription1End).toBeCalledTimes(1); - expect(handleSubscription2End).toBeCalledTimes(1); - }); - - test("Persistent subscription to stream", async () => { - const defer = new Defer(); - const STREAM_NAME = uuid(); - const GROUP_NAME = uuid(); - const STREAM_NAME_2 = uuid(); - const GROUP_NAME_2 = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME_2, - GROUP_NAME_2, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(); - const handlePS$allSubscription1End = jest.fn(defer.resolve); - const handlePS$allSubscription2End = jest.fn(defer.resolve); - - client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlePS$allSubscription1End); - - client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME_2, GROUP_NAME_2) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlePS$allSubscription2End); - - await delay(500); - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - await client.dispose(); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handlePS$allSubscription1End).toBeCalledTimes(1); - expect(handlePS$allSubscription2End).toBeCalledTimes(1); - }); - - test("read $all", async () => { - const defer = new Defer(); - const STREAM_NAME = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(); - const handlereadAll1End = jest.fn(defer.resolve); - const handlereadAll2End = jest.fn(defer.resolve); - - client - .readAll() - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlereadAll1End); - - client - .readAll() - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlereadAll2End); - - await delay(500); - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - await client.dispose(); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handlereadAll1End).toBeCalledTimes(1); - expect(handlereadAll2End).toBeCalledTimes(1); - }); - - test("read stream", async () => { - const defer = new Defer(); - const STREAM_NAME = uuid(); - const STREAM_NAME_2 = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(100)); - await client.appendToStream(STREAM_NAME_2, jsonTestEvents(100)); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(); - const handlereadStream1End = jest.fn(defer.resolve); - const handlereadStream2End = jest.fn(defer.resolve); - - client - .readStream(STREAM_NAME) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlereadStream1End); - - client - .readStream(STREAM_NAME_2) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlereadStream2End); - - await delay(10); - - await client.dispose(); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handlereadStream1End).toBeCalledTimes(1); - expect(handlereadStream2End).toBeCalledTimes(1); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - test("Batch append", async () => { - const STREAM_NAME = uuid(); - const result = await client.appendToStream(STREAM_NAME, jsonTestEvents()); - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - await client.dispose(); - const result2 = await client.appendToStream( - STREAM_NAME, - jsonTestEvents() - ); - expect(result2).toBeDefined(); - expect(result2.nextExpectedRevision).toBeGreaterThanOrEqual(0); - }); - - test("Persistent subscription to $all", async () => { - const defer = new Defer(); - const STREAM_NAME = uuid(); - const GROUP_NAME = uuid(); - const GROUP_NAME_2 = uuid(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { filter: streamNameFilter({ prefixes: [STREAM_NAME] }) } - ); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME_2, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }) - ); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(); - const handlePS$allSubscription1End = jest.fn(defer.resolve); - const handlePS$allSubscription2End = jest.fn(defer.resolve); - - client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlePS$allSubscription1End); - - client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME_2) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handlePS$allSubscription2End); - - await delay(500); - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - await client.dispose(); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handlePS$allSubscription1End).toBeCalledTimes(1); - expect(handlePS$allSubscription2End).toBeCalledTimes(1); - }); - }); -}); - -function extractKnownStreams(this: EventStoreDBClient): Set { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (this as any).disposableStreams; -} diff --git a/packages/test/src/extra/encoding.test.ts b/packages/test/src/extra/encoding.test.ts deleted file mode 100644 index 3a4e4543..00000000 --- a/packages/test/src/extra/encoding.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { createTestNode } from "@test-utils"; -import { - EventStoreDBClient, - jsonEvent, - ResolvedEvent, - START, -} from "@eventstore/db-client"; - -describe("encoding", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("U+0018", async () => { - const STREAM_NAME = "cancel"; - - const cancelEvent = jsonEvent({ - type: "something", - data: { - char: "", - }, - }); - - await client.appendToStream(STREAM_NAME, cancelEvent); - - let resolvedEvent!: ResolvedEvent; - - for await (const event of client.readStream(STREAM_NAME, { - maxCount: 1, - fromRevision: START, - })) { - resolvedEvent = event; - } - - const event = resolvedEvent.event!; - - expect(event.data).toMatchObject({ - char: "", - }); - }); - - test("U+0118", async () => { - const STREAM_NAME = "ogonek"; - - const eEvent = jsonEvent({ - type: "something", - data: { - char: "Ę", - }, - }); - - await client.appendToStream(STREAM_NAME, eEvent); - - let resolvedEvent!: ResolvedEvent; - - for await (const event of client.readStream(STREAM_NAME, { - maxCount: 1, - fromRevision: START, - })) { - resolvedEvent = event; - } - - const event = resolvedEvent.event!; - - expect(event.data).toMatchObject({ - char: "Ę", - }); - }); -}); diff --git a/packages/test/src/extra/http.test.ts b/packages/test/src/extra/http.test.ts deleted file mode 100644 index 5175d514..00000000 --- a/packages/test/src/extra/http.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { createInsecureTestCluster, createTestCluster } from "@test-utils"; -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("http api", () => { - interface PingResult { - msgTypeId: number; - text: string; - } - function ping(this: EventStoreDBClient) { - return this.HTTPRequest("GET", "/ping", {}); - } - const goodPing = { - msgTypeId: expect.any(Number), - text: "Ping request successfully handled", - }; - - describe("secure", () => { - const cluster = createTestCluster(); - - beforeAll(async () => { - await cluster.up(); - }); - - afterAll(async () => { - await cluster.down(); - }); - - test("dns", async () => { - const client = new EventStoreDBClient( - { endpoints: cluster.endpoints }, - { rootCertificate: cluster.certs.root } - ); - - const result = await ping.call(client); - expect(result).toMatchObject(goodPing); - }); - - test("ip", async () => { - const client = new EventStoreDBClient( - { - endpoints: cluster.endpoints.map(({ address: _, port }) => ({ - address: "127.0.0.1", - port, - })), - }, - { rootCertificate: cluster.certs.root } - ); - - const result = await ping.call(client); - expect(result).toMatchObject(goodPing); - }); - - test("error transform", async () => { - const client = new EventStoreDBClient( - { endpoints: cluster.endpoints }, - { rootCertificate: cluster.certs.root } - ); - - class TestError extends Error { - public code: number; - constructor(code: number, message: string) { - super(message); - this.code = code; - } - } - - function nonsense(this: EventStoreDBClient) { - return this.HTTPRequest("POST", "/asdpoijsad", { - transformError: (statusCode, statusMessage) => { - if (statusCode === 404) { - return new TestError(statusCode, statusMessage); - } - }, - }); - } - - try { - const response = await nonsense.call(client); - expect(response).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(TestError); - expect(error).toMatchInlineSnapshot(`[Error: Not Found]`); - } - }); - }); - - describe("insecure", () => { - const cluster = createInsecureTestCluster(); - - beforeAll(async () => { - await cluster.up(); - }); - - afterAll(async () => { - await cluster.down(); - }); - - test("dns", async () => { - const client = new EventStoreDBClient( - { endpoints: cluster.endpoints }, - { insecure: true } - ); - - const result = await ping.call(client); - expect(result).toMatchObject(goodPing); - }); - - test("ip", async () => { - const client = new EventStoreDBClient( - { - endpoints: cluster.endpoints.map(({ address: _, port }) => ({ - address: "127.0.0.1", - port, - })), - }, - { insecure: true } - ); - - const result = await ping.call(client); - expect(result).toMatchObject(goodPing); - }); - - test("error transform", async () => { - const client = new EventStoreDBClient( - { endpoints: cluster.endpoints }, - { insecure: true } - ); - - class TestError extends Error { - public code: number; - constructor(code: number, message: string) { - super(message); - this.code = code; - } - } - - function nonsense(this: EventStoreDBClient) { - return this.HTTPRequest("POST", "/asdpoijsad", { - transformError: (statusCode, statusMessage) => { - if (statusCode === 404) { - return new TestError(statusCode, statusMessage); - } - }, - }); - } - - try { - const response = await nonsense.call(client); - expect(response).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(TestError); - expect(error).toMatchInlineSnapshot(`[Error: Not Found]`); - } - }); - }); -}); diff --git a/packages/test/src/extra/http2-assertion-failure.test.ts b/packages/test/src/extra/http2-assertion-failure.test.ts deleted file mode 100644 index a813dae8..00000000 --- a/packages/test/src/extra/http2-assertion-failure.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { v4 as uuid } from "uuid"; -import { createInsecureTestNode, delay, jsonTestEvents } from "@test-utils"; -import { - EventStoreDBClient, - NO_STREAM, - ResolvedEvent, - START, -} from "@eventstore/db-client"; - -describe("http2 assertion failure", () => { - const node = createInsecureTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient({ endpoint: node.uri }, { insecure: true }); - }); - - afterAll(async () => { - await node.down(); - }); - - it("can subscribe to a stream", async () => { - for (let i = 0; i < 80; i++) { - const stream = `test_${uuid().replace(/-/g, "")}`; - const priorEvents = jsonTestEvents(3); - const postEvents = jsonTestEvents(7); - - const appendRes = await client.appendToStream(stream, priorEvents, { - expectedRevision: NO_STREAM, - // we want to test classic append - credentials: { username: "admin", password: "changeit" }, - }); - - const received: ResolvedEvent[] = []; - const sub = client.subscribeToStream(stream, { - fromRevision: START, - }); - sub.on("error", (err) => { - console.log(err, err?.message); - throw err; - }); - sub.on("close", () => { - // subscription stopped - }); - sub.on("data", (evt) => { - received.push(evt); - }); - while (received.length < 3) await delay(10); - await client.appendToStream(stream, postEvents, { - expectedRevision: appendRes.nextExpectedRevision, - // we want to test classic append - credentials: { username: "admin", password: "changeit" }, - }); - - while (received.length < 10) await delay(10); - expect(received.map((e) => e.event?.id)).toStrictEqual( - [...priorEvents, ...postEvents].map((e) => e.id) - ); - await sub.unsubscribe(); - } - }); -}); diff --git a/packages/test/src/extra/oversize-event.test.ts b/packages/test/src/extra/oversize-event.test.ts deleted file mode 100644 index 3895c2dc..00000000 --- a/packages/test/src/extra/oversize-event.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { createTestNode, delay, jsonTestEvents } from "@test-utils"; -import { - EventStoreDBClient, - jsonEvent, - JSONEventType, - RUNNING, -} from "@eventstore/db-client"; - -describe("oversize events", () => { - const node = createTestNode(); - - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - // await node.openInBrowser(false); - await node.down(); - }); - - test("oversize events", async () => { - const STREAM_NAME = "big_event"; - const TRIGGER = "big_event_trigger"; - const FINISH_TEST = "big_event_finish_test"; - const PROJECTION_NAME = "big_event_projection"; - const doSomething = jest.fn(); - - const BIG_EVENT = "big_event"; - const bigData = "r" + "e".repeat(15 * 1024 * 1024); - - type BigEvent = JSONEventType< - typeof BIG_EVENT, - { message: string }, - { message: string } - >; - - const projection = ` - fromStream("${STREAM_NAME}") - .when({ - $init() { - return 0; - }, - ${TRIGGER}() { - emit("${STREAM_NAME}_emit", "${BIG_EVENT}", { message: "r" + "e".repeat(15 * 1024 * 1024) }, {}); - } - }); -`; - - await client.createProjection(PROJECTION_NAME, projection, { - emitEnabled: true, - }); - - // give it a chance to get up and running - await delay(1000); - - const state = await client.getProjectionStatus(PROJECTION_NAME); - expect(state.projectionStatus).toBe(RUNNING); - - await client.appendToStream(STREAM_NAME, [ - jsonEvent({ type: TRIGGER, data: "hi" }), - ...jsonTestEvents(5), - jsonEvent({ type: FINISH_TEST, data: "hi" }), - ]); - - // check that the big event hasn't blown it up - const state2 = await client.getProjectionStatus(PROJECTION_NAME); - expect(state2.projectionStatus).toBe(RUNNING); - - const subscription = client.subscribeToStream( - `${STREAM_NAME}_emit` - ); - - for await (const { event } of subscription) { - if (event?.type !== BIG_EVENT) continue; - doSomething(event); - expect(event.data.message).toBe(bigData); - break; - } - - expect(doSomething).toBeCalled(); - }); -}); diff --git a/packages/test/src/extra/typedEvents-more.test.ts b/packages/test/src/extra/typedEvents-more.test.ts deleted file mode 100644 index ec4655be..00000000 --- a/packages/test/src/extra/typedEvents-more.test.ts +++ /dev/null @@ -1,319 +0,0 @@ -import { v4 as uuid } from "uuid"; - -import { createTestNode } from "@test-utils"; -import { - AppendResult, - EventStoreDBClient, - EventType, - jsonEvent, - JSONEventType, - RecordedEvent, - ResolvedEvent, - StreamingRead, -} from "@eventstore/db-client"; - -describe("typed events should compile", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("stream agregator", async () => { - type EventAggregator = ( - currentState: Aggregate | undefined, - event: RecordedEvent - ) => Aggregate; - - const createStreamAggregator = - ( - when: EventAggregator - ) => - async ( - eventStream: StreamingRead> - ): Promise => { - let currentState: Entity | undefined = undefined; - for await (const { event } of eventStream) { - if (!event) continue; - currentState = when(currentState, event); - } - if (currentState == null) throw "oh no"; - return currentState; - }; - - interface ProductItem { - productId: string; - quantity: number; - } - - // bare interface - interface ShoppingCartOpened { - type: "shopping-cart-opened"; - data: { - shoppingCartId: string; - clientId: string; - openedAt: string; - }; - } - - // using JSONEventType - type ProductItemAddedToShoppingCart = JSONEventType< - "product-item-added-to-shopping-cart", - { - shoppingCartId: string; - productItem: ProductItem; - } - >; - - type ProductItemRemovedFromShoppingCart = JSONEventType< - "product-item-removed-from-shopping-cart", - { - shoppingCartId: string; - productItem: ProductItem; - } - >; - - type ShoppingCartConfirmed = JSONEventType< - "shopping-cart-confirmed", - { - shoppingCartId: string; - confirmedAt: string; - } - >; - - type ShoppingCartEvent = - | ShoppingCartOpened - | ProductItemAddedToShoppingCart - | ProductItemRemovedFromShoppingCart - | ShoppingCartConfirmed; - - enum ShoppingCartStatus { - Opened = 1, - Confirmed = 2, - Cancelled = 4, - Closed = Confirmed | Cancelled, - } - - type ProductItems = Map; - - interface ShoppingCart { - id: string; - clientId: string; - status: ShoppingCartStatus; - productItems: ProductItems; - openedAt: Date; - confirmedAt?: Date; - } - - const enum ShoppingCartErrors { - OPENED_EXISTING_CART = "OPENED_EXISTING_CART", - CART_NOT_FOUND = "CART_NOT_FOUND", - PRODUCT_ITEM_NOT_FOUND = "PRODUCT_ITEM_NOT_FOUND", - UNKNOWN_EVENT_TYPE = "UNKNOWN_EVENT_TYPE", - } - - const addProductItem = ( - inventory: ProductItems, - { productId, quantity }: ProductItem - ): ProductItems => { - const current = inventory.get(productId); - - if (!current) { - return inventory.set(productId, { productId, quantity }); - } - - return inventory.set(productId, { - ...current, - quantity: current.quantity + quantity, - }); - }; - - const removeProductItem = ( - inventory: ProductItems, - { productId, quantity }: ProductItem - ): ProductItems => { - const current = inventory.get(productId); - - if (!current || current.quantity < quantity) { - throw ShoppingCartErrors.PRODUCT_ITEM_NOT_FOUND; - } - - if (current.quantity === quantity) { - inventory.delete(productId); - return inventory; - } - - return inventory.set(productId, { - ...current, - quantity: current.quantity - quantity, - }); - }; - - const create = - ( - client: EventStoreDBClient, - handle: (command: Command) => StreamEvent - ) => - (streamName: string, command: Command): Promise => { - const event = handle(command); - const eventData = jsonEvent(event); - return client.appendToStream(streamName, eventData); - }; - - create(client, () => ({ - type: "shopping-cart-opened", - data: { - shoppingCartId: uuid(), - clientId: uuid(), - openedAt: new Date().toJSON(), - }, - })); - - const shoppingCartAggregator = createStreamAggregator< - ShoppingCart, - ShoppingCartEvent - >((currentState, event) => { - if (event.type === "shopping-cart-opened") { - if (currentState != null) throw ShoppingCartErrors.OPENED_EXISTING_CART; - return { - id: event.data.shoppingCartId, - clientId: event.data.clientId, - openedAt: new Date(event.data.openedAt), - productItems: new Map(), - status: ShoppingCartStatus.Opened, - }; - } - - if (currentState == null) throw ShoppingCartErrors.CART_NOT_FOUND; - - switch (event.type) { - case "product-item-added-to-shopping-cart": - return { - ...currentState, - productItems: addProductItem( - currentState.productItems, - event.data.productItem - ), - }; - case "product-item-removed-from-shopping-cart": - return { - ...currentState, - productItems: removeProductItem( - currentState.productItems, - event.data.productItem - ), - }; - case "shopping-cart-confirmed": - return { - ...currentState, - status: ShoppingCartStatus.Confirmed, - confirmedAt: new Date(event.data.confirmedAt), - }; - default: { - const _: never = event; - throw ShoppingCartErrors.UNKNOWN_EVENT_TYPE; - } - } - }); - - const enum ProductsIds { - T_SHIRT = "team-building-excercise-2022", - SHOES = "air-jordan", - } - - const clientId = "client_123"; - const shoppingCartId = "cart_456"; - const events: ShoppingCartEvent[] = [ - { - type: "shopping-cart-opened", - data: { - shoppingCartId, - clientId, - openedAt: new Date().toJSON(), - }, - }, - { - type: "product-item-added-to-shopping-cart", - data: { - shoppingCartId, - productItem: { - productId: ProductsIds.SHOES, - quantity: 100, - }, - }, - }, - { - type: "product-item-added-to-shopping-cart", - data: { - shoppingCartId, - productItem: { - productId: ProductsIds.T_SHIRT, - quantity: 1, - }, - }, - }, - { - type: "product-item-removed-from-shopping-cart", - data: { - shoppingCartId, - productItem: { - productId: ProductsIds.SHOES, - quantity: 100, - }, - }, - }, - { - type: "product-item-added-to-shopping-cart", - data: { - shoppingCartId, - productItem: { - productId: ProductsIds.T_SHIRT, - quantity: 1, - }, - }, - }, - { - type: "shopping-cart-confirmed", - data: { - shoppingCartId, - confirmedAt: new Date().toJSON(), - }, - }, - ]; - - const jsonEvents = events.map((e) => jsonEvent(e)); - - await client.appendToStream( - `shoppingcart-${shoppingCartId}`, - jsonEvents - ); - - const shoppingCartStream = client.readStream( - `shoppingcart-${shoppingCartId}` - ); - - const cart = await shoppingCartAggregator(shoppingCartStream); - - expect(cart).toMatchObject({ - id: shoppingCartId, - clientId, - openedAt: expect.any(Date), - status: ShoppingCartStatus.Confirmed, - confirmedAt: expect.any(Date), - }); - - expect(Array.from(cart.productItems)).toEqual([ - [ProductsIds.T_SHIRT, { productId: ProductsIds.T_SHIRT, quantity: 2 }], - ]); - }); -}); diff --git a/packages/test/src/extra/typedEvents.test.ts b/packages/test/src/extra/typedEvents.test.ts deleted file mode 100644 index 7f19ef0e..00000000 --- a/packages/test/src/extra/typedEvents.test.ts +++ /dev/null @@ -1,334 +0,0 @@ -import { createTestNode } from "@test-utils"; -import { - binaryEvent, - BinaryEventType, - EventStoreDBClient, - jsonEvent, - JSONEventType, - persistentSubscriptionToStreamSettingsFromDefaults, -} from "@eventstore/db-client"; - -describe("typed events should compile", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("jsonEvent", () => { - type MyFirstEvent = JSONEventType< - "my-first-event", - { hello: string }, - { meta: string } - >; - - jsonEvent({ - type: "my-first-event", - data: { hello: "hi" }, - metadata: { meta: "data" }, - }); - - jsonEvent( - // @ts-expect-error metadata is required in the event type - { - type: "my-first-event", - data: { hello: "hi" }, - } - ); - - jsonEvent({ - // @ts-expect-error wrong type - type: "my-second-event", - data: { hello: "hi" }, - metadata: { meta: "data" }, - }); - - type MyOtherEvent = JSONEventType<"my-other-event", { hello: string }>; - - // not specifying metadata in type means it is optional - jsonEvent({ - type: "my-other-event", - data: { hello: "hi" }, - }); - - jsonEvent({ - type: "my-other-event", - data: { hello: "hi" }, - metadata: { however: "I can pass something, if I need" }, - }); - - // dont have to pass type - jsonEvent({ - type: "any_event", - data: { hello: "hi" }, - metadata: { meta: "data" }, - }); - - // metadata is optional by default - jsonEvent({ - type: "any_event", - data: { hello: "hi" }, - }); - - // @ts-expect-error wont take a BinaryEventType - jsonEvent({ - type: "any_event", - data: { hello: "hi" }, - }); - }); - - test("binaryEvent", () => { - type MySecondEvent = BinaryEventType<"my-second-event", { meta: string }>; - - binaryEvent({ - type: "my-second-event", - data: new Uint8Array(), - metadata: { meta: "data" }, - }); - - binaryEvent( - // @ts-expect-error metadata is required in the event type - { - type: "my-second-event", - data: new Uint8Array(), - } - ); - - binaryEvent({ - // @ts-expect-error wrong type - type: "my-first-event", - data: new Uint8Array(), - metadata: { meta: "data" }, - }); - - type MyOtherEvent = BinaryEventType<"my-other-event">; - - // not specifying metadata in type means it is optional - binaryEvent({ - type: "my-other-event", - data: new Uint8Array(), - }); - - binaryEvent({ - type: "my-other-event", - data: new Uint8Array(), - metadata: { however: "I can pass something, if I need" }, - }); - - // dont have to pass type - binaryEvent({ - type: "any_event", - data: new Uint8Array(), - metadata: { meta: "data" }, - }); - - // metadata is optional by default - binaryEvent({ - type: "any_event", - data: new Uint8Array(), - }); - - // @ts-expect-error wont take a JSONEventType - binaryEvent({ - type: "any_event", - data: new Uint8Array(), - }); - }); - - test("readStream", async () => { - const STREAM_NAME = "known_types_in_stream"; - - // set up event types - - type MyEvent = JSONEventType< - "my-event", - { hello: string }, - { meta: string } - >; - - type MyOtherEvent = JSONEventType< - "my-other-event", - { hi: string }, - { metadata: string } - >; - - type KnownEvents = MyEvent | MyOtherEvent; - - const event1 = jsonEvent({ - type: "my-event", - data: { hello: "hi there" }, - metadata: { meta: "data" }, - }); - - const event2 = jsonEvent({ - type: "my-other-event", - data: { hi: "hello there" }, - metadata: { metadata: "goes here" }, - }); - - await client.appendToStream(STREAM_NAME, [event1, event2]); - - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - switch (event?.type) { - case "my-event": - // `hello` exists on the data of `my-event` so we can access it deirectly - expect(event.data.hello).toBeDefined(); - break; - case "my-other-event": - // @ts-expect-error `hello` doesnt exist on `my-other-event`, so this errors - expect(event.data.hello).not.toBeDefined(); - break; - } - } - }); - - test("subscribeToStream", async () => { - const STREAM_NAME = "known_types_in_stream_subscription"; - - // set up event types - - type MyGreatEvent = JSONEventType< - "my-great-event", - { yay: true; reason: string }, - { feels: string } - >; - - enum WhatWentWrong { - "bad-stuff", - "kinda-bad-stuff", - } - - type MyNotSoGreatEvent = JSONEventType< - "my-not-so-great-event", - { ohno: WhatWentWrong }, - { feels: string } - >; - - type KnownEvents = MyGreatEvent | MyNotSoGreatEvent; - - const event1 = jsonEvent({ - type: "my-great-event", - data: { yay: true, reason: "I wrote an event!" }, - metadata: { feels: "great" }, - }); - - const event2 = jsonEvent({ - type: "my-not-so-great-event", - data: { ohno: WhatWentWrong["kinda-bad-stuff"] }, - metadata: { feels: "terrible" }, - }); - - await client.appendToStream(STREAM_NAME, [event1, event2]); - - for await (const { event } of client.subscribeToStream( - STREAM_NAME - )) { - switch (event?.type) { - case "my-great-event": - // `yay` exists on the data of `my-event` so we can access it deirectly - expect(event.data.yay).toBeDefined(); - // same here - expect(event.metadata.feels).toBeDefined(); - break; - case "my-not-so-great-event": - // jest infers this type as being `WhatWentWrong` so we can see that enum types work - expect(event.data.ohno).not.toBe(WhatWentWrong["kinda-bad-stuff"]); - break; - // @ts-expect-error this type doesnt exist - case "my-other-event": - // event is never - expect(event).not.toBeDefined(); - break; - } - - // we only care about the TS here - break; - } - }); - - test("connectToPersistantSubscription", async () => { - const STREAM_NAME = "known_types_in_persistent_subscription"; - const GROUP_NAME = "some_group"; - - // set up event types - - type MyGreatEvent = JSONEventType< - "my-great-event", - { yay: true; reason: string }, - { feels: string } - >; - - enum WhatWentWrong { - "bad-stuff", - "kinda-bad-stuff", - } - - type MyNotSoGreatEvent = JSONEventType< - "my-not-so-great-event", - { ohno: WhatWentWrong }, - { feels: string } - >; - - type KnownEvents = MyGreatEvent | MyNotSoGreatEvent; - - const event1 = jsonEvent({ - type: "my-great-event", - data: { yay: true, reason: "I wrote an event!" }, - metadata: { feels: "great" }, - }); - - const event2 = jsonEvent({ - type: "my-not-so-great-event", - data: { ohno: WhatWentWrong["kinda-bad-stuff"] }, - metadata: { feels: "terrible" }, - }); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ); - - await client.appendToStream(STREAM_NAME, [event1, event2]); - - for await (const { - event, - } of client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - )) { - switch (event?.type) { - case "my-great-event": - // `yay` exists on the data of `my-event` so we can access it deirectly - expect(event.data.yay).toBeDefined(); - // same here - expect(event.metadata.feels).toBeDefined(); - break; - case "my-not-so-great-event": - // jest infers this type as being `WhatWentWrong` so we can see that enum types work - expect(event.data.ohno).not.toBe(WhatWentWrong["kinda-bad-stuff"]); - break; - // @ts-expect-error this type doesnt exist - case "my-other-event": - // event is never - expect(event).not.toBeDefined(); - break; - } - - // we only care about the TS here - break; - } - }); -}); diff --git a/packages/test/src/extra/write-after-end.test.ts b/packages/test/src/extra/write-after-end.test.ts deleted file mode 100644 index 38b7ec26..00000000 --- a/packages/test/src/extra/write-after-end.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - delay, - jsonTestEvents, - matchServerVersion, - optionalTest, -} from "@test-utils"; -import { - CancelledError, - EventData, - EventStoreDBClient, - jsonEvent, - UnavailableError, -} from "@eventstore/db-client"; - -// These tests can take time. -jest.setTimeout(120_000); - -const neverEndingEvents: Array = (function* neverEndingEvents() { - let i = 0; - while (true) { - yield jsonEvent({ - type: "test", - data: { - message: "test", - index: i++, - }, - }); - } -})() as never; - -// neverEndingEvents really is an array... -const isArray = Array.isArray; -Array.isArray = (arg): arg is never[] => { - if (isArray(arg)) return true; - if (arg === neverEndingEvents) return true; - return false; -}; - -describe("write after end", () => { - test("Should not write after end", async () => { - // We are going to do a huge append, so tell eventstore not to reject it - const node = createTestNode().setOption( - "EVENTSTORE_MAX_APPEND_SIZE", - 10_000_000 - ); - await node.up(); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const STREAM_NAME = "json_stream_name"; - await client.appendToStream(STREAM_NAME, jsonTestEvents(), { - // credentials enforces classic append - credentials: { username: "admin", password: "changeit" }, - }); - - const neverEndingAppend = client - .appendToStream(STREAM_NAME, neverEndingEvents, { - // credentials enforces classic append - credentials: { username: "admin", password: "changeit" }, - deadline: Infinity, - }) - .catch((err) => err); - - // let the write get started - await delay(1); - - await node.killNode(node.endpoints[0]); - - const error = await neverEndingAppend; - expect(error).toBeInstanceOf(UnavailableError); - - // wait for any unhandled rejections - await delay(5_000); - - await node.down(); - }); - - optionalTest(matchServerVersion`>=21.10`)( - "Should not write after end (batch append)", - async () => { - const node = createTestNode(); - await node.up(); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const STREAM_NAME = "json_stream_name"; - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - const writeUntilError = () => - new Promise((resolve) => { - const writeOnLoop = (): Promise => - client - .appendToStream(STREAM_NAME, jsonTestEvents(30_000)) - .then(writeOnLoop); - - writeOnLoop().catch((e) => { - resolve(e); - }); - }); - - const errorPromise = writeUntilError(); - - await node.killNode(node.endpoints[0]); - - const error = await errorPromise; - - expect(error).toBeInstanceOf(CancelledError); - - // wait for any unhandled rejections - await delay(5_000); - } - ); -}); diff --git a/packages/test/src/opentelemetry/instrumentation.test.ts b/packages/test/src/opentelemetry/instrumentation.test.ts deleted file mode 100644 index a2622190..00000000 --- a/packages/test/src/opentelemetry/instrumentation.test.ts +++ /dev/null @@ -1,540 +0,0 @@ -import { createTestNode, Defer, delay, jsonTestEvents } from "@test-utils"; -import { - NodeTracerProvider, - InMemorySpanExporter, - SimpleSpanProcessor, -} from "@opentelemetry/sdk-trace-node"; -import { - SEMATTRS_EXCEPTION_STACKTRACE, - SEMATTRS_EXCEPTION_TYPE, -} from "@opentelemetry/semantic-conventions"; -import { EventStoreDBInstrumentation } from "@eventstore/opentelemetry"; -import { EventStoreDBAttributes } from "@eventstore/opentelemetry/dist/attributes"; -import { v4 } from "uuid"; -import { collect } from "@test-utils"; - -const tracerProvider = new NodeTracerProvider(); -tracerProvider.register(); - -const instrumentation = new EventStoreDBInstrumentation(); -instrumentation.disable(); - -import * as esdb from "@eventstore/db-client"; -import { - AppendToStreamOptions, - ResolvedEvent, - streamNameFilter, - WrongExpectedVersionError, -} from "@eventstore/db-client"; - -describe("instrumentation", () => { - const node = createTestNode(); - const moduleName = "@eventstore/opentelemetry"; - - const memoryExporter = new InMemorySpanExporter(); - instrumentation.setTracerProvider(tracerProvider); - tracerProvider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - - // @ts-expect-error the moduleExports property is private. This is needed to make the test work with auto-mocking - instrumentation._modules[0].moduleExports = esdb; - - beforeAll(async () => { - await node.up(); - instrumentation.enable(); - }); - - afterAll(async () => { - instrumentation.disable(); - await node.down(); - }); - - afterEach(() => { - memoryExporter.reset(); - }); - - describe("append", () => { - test.each([ - { withCredentials: false, credentials: undefined }, - { - withCredentials: true, - credentials: { username: "admin", password: "changeit" }, - }, - ])( - "should create a span for append operation, withCredentials: $withCredentials", - async ({ withCredentials, credentials }) => { - const { EventStoreDBClient, jsonEvent } = await import( - "@eventstore/db-client" - ); - - const STREAM = v4(); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const appendOptions: AppendToStreamOptions = { - expectedRevision: "any", - }; - - if (withCredentials) { - appendOptions.credentials = credentials; - } - - await client.appendToStream( - STREAM, - jsonEvent({ - type: "test", - data: {}, - }), - appendOptions - ); - - const spans = memoryExporter.getFinishedSpans(); - const span = spans[0]; - - const events = await collect(client.readStream(STREAM)); - const event = events[0]; - - expect(events.length).toBe(1); - expect(event.event?.metadata).toStrictEqual({ - $traceId: expect.any(String), - $spanId: expect.any(String), - }); - - const expectedAttributes = { - [EventStoreDBAttributes.EVENT_STORE_STREAM]: STREAM, - [EventStoreDBAttributes.SERVER_ADDRESS]: node.endpoints[0].address, - [EventStoreDBAttributes.SERVER_PORT]: - node.endpoints[0].port.toString(), - [EventStoreDBAttributes.DATABASE_SYSTEM]: moduleName, - [EventStoreDBAttributes.DATABASE_OPERATION]: "appendToStream", - }; - - if (withCredentials) { - expectedAttributes[EventStoreDBAttributes.DATABASE_USER] = - credentials!.username; - } - - expect(spans.length).toBe(1); - expect(span.attributes).toStrictEqual(expectedAttributes); - } - ); - - test("span contains error when append fails", async () => { - const { EventStoreDBClient } = await import("@eventstore/db-client"); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const STREAM_NAME = v4(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: "no_stream", - } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - expect(spans.length).toBe(2); - - const failedSpan = spans[1]; - - const failedEvents = failedSpan.events; - - expect(failedEvents.length).toBe(1); - - const failedEvent = failedEvents[0]; - - if (error instanceof WrongExpectedVersionError) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - expect(failedEvent).toEqual( - expect.objectContaining({ - name: "exception", - attributes: { - [SEMATTRS_EXCEPTION_TYPE]: "Error", - [SEMATTRS_EXCEPTION_STACKTRACE]: error.stack, - }, - }) - ); - } - } - }); - }); - - describe("catch up subscriptions", () => { - test("should create child span in subscription to stream", async () => { - const defer = new Defer(); - const { EventStoreDBClient, jsonEvent } = await import( - "@eventstore/db-client" - ); - - const STREAM = v4(); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn((event: ResolvedEvent) => { - if (event.event?.streamId == STREAM) { - subscription.unsubscribe(); - } - }); - const handleEnd = jest.fn(defer.resolve); - const handleConfirmation = jest.fn(); - - const event = jsonEvent({ - type: "SomeType", - data: {}, - }); - - await client.appendToStream(STREAM, event); - - const subscription = client - .subscribeToStream(STREAM, { - credentials: { - username: "admin", - password: "changeit", - }, - }) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handleEnd) - .on("confirmation", handleConfirmation); - - await delay(500); - await defer.promise; - - const spans = memoryExporter.getFinishedSpans(); - - const parentSpan = spans.find( - (span) => span.name === EventStoreDBAttributes.STREAM_APPEND - ); - const childSpan = spans.find( - (span) => span.name === EventStoreDBAttributes.STREAM_SUBSCIBE - ); - - expect(handleConfirmation).toHaveBeenCalledTimes(1); - - expect(parentSpan).toBeDefined(); - expect(childSpan).toBeDefined(); - expect(parentSpan?.spanContext().spanId).toBe(childSpan?.parentSpanId); - - expect(childSpan?.attributes).toMatchObject({ - [EventStoreDBAttributes.EVENT_STORE_STREAM]: STREAM, - [EventStoreDBAttributes.EVENT_STORE_EVENT_ID]: event.id, - [EventStoreDBAttributes.EVENT_STORE_EVENT_TYPE]: event.type, - [EventStoreDBAttributes.EVENT_STORE_SUBSCRIPTION_ID]: subscription.id, - [EventStoreDBAttributes.SERVER_ADDRESS]: node.endpoints[0].address, - [EventStoreDBAttributes.SERVER_PORT]: node.endpoints[0].port.toString(), - [EventStoreDBAttributes.DATABASE_SYSTEM]: moduleName, - [EventStoreDBAttributes.DATABASE_OPERATION]: "subscribeToStream", - [EventStoreDBAttributes.DATABASE_USER]: "admin", - }); - - expect(parentSpan?.attributes).toMatchObject({ - [EventStoreDBAttributes.EVENT_STORE_STREAM]: STREAM, - [EventStoreDBAttributes.SERVER_ADDRESS]: node.endpoints[0].address, - [EventStoreDBAttributes.SERVER_PORT]: node.endpoints[0].port.toString(), - [EventStoreDBAttributes.DATABASE_SYSTEM]: moduleName, - [EventStoreDBAttributes.DATABASE_OPERATION]: "appendToStream", - }); - }); - - test.only("events with non-json metadata are not traced in subscriptions", async () => { - const defer = new Defer(); - const { EventStoreDBClient, jsonEvent, binaryEvent } = await import( - "@eventstore/db-client" - ); - - const STREAM = v4(); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn((event: ResolvedEvent) => { - if (event.event?.streamId == STREAM) { - subscription.unsubscribe(); - } - }); - const handleEnd = jest.fn(defer.resolve); - const handleConfirmation = jest.fn(); - - const event1 = binaryEvent({ - type: "SomeType", - data: Buffer.from("hello"), - metadata: { - "some-data": "some-value", - }, - }); - const event2 = jsonEvent({ - type: "SomeType", - data: { - "some-data": "some-value", - }, - metadata: 2, - }); - - await client.appendToStream(STREAM, [event1, event2]); - - const subscription = client - .subscribeToStream(STREAM, { - credentials: { - username: "admin", - password: "changeit", - }, - }) - .on("error", handleError) - .on("data", handleEvent) - .on("end", handleEnd) - .on("confirmation", handleConfirmation); - - await delay(500); - await defer.promise; - - const spans = memoryExporter.getFinishedSpans(); - - const parentSpans = spans.filter( - (span) => span.name === EventStoreDBAttributes.STREAM_APPEND - ); - - const childSpans = spans.filter( - (span) => span.name === EventStoreDBAttributes.STREAM_SUBSCIBE - ); - - expect(handleConfirmation).toHaveBeenCalledTimes(1); - - expect(parentSpans.length).toBe(1); - - expect(childSpans).toBeDefined(); - - expect(childSpans).toHaveLength(1); - - expect( - childSpans[0].attributes[EventStoreDBAttributes.EVENT_STORE_EVENT_ID] - ).toBe(event1.id); - expect( - childSpans[0].attributes[EventStoreDBAttributes.EVENT_STORE_EVENT_TYPE] - ).toBe(event1.type); - }); - }); - - describe("persistent subscriptions", () => { - test("should create child span in persistent subscription to stream", async () => { - const { - EventStoreDBClient, - jsonEvent, - persistentSubscriptionToStreamSettingsFromDefaults, - START, - } = await import("@eventstore/db-client"); - - const STREAM = v4(); - const GROUP = v4(); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.createPersistentSubscriptionToStream( - STREAM, - GROUP, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - const defer = new Defer(); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(async (event: ResolvedEvent) => { - if (event.event) { - await subscription.ack(event); - defer.resolve(); - } - }); - const handleEnd = jest.fn(defer.resolve); - const onConfirmation = jest.fn(); - - const subscription = client - .subscribeToPersistentSubscriptionToStream(STREAM, GROUP) - .on("error", handleError) - .on("data", handleEvent) - .on("confirmation", onConfirmation) - .on("end", handleEnd); - - const event = jsonEvent({ - type: "SomeEvent", - data: {}, - }); - - await client.appendToStream(STREAM, event); - - await delay(500); - await defer.promise; - - const spans = memoryExporter.getFinishedSpans(); - - expect(handleEvent).toHaveBeenCalledTimes(1); - - const parentSpan = spans.find( - (span) => span.name === EventStoreDBAttributes.STREAM_APPEND - ); - const childSpan = spans.find( - (span) => span.name === EventStoreDBAttributes.STREAM_SUBSCIBE - ); - - expect(parentSpan).toBeDefined(); - expect(childSpan).toBeDefined(); - expect(parentSpan?.spanContext().spanId).toBe(childSpan?.parentSpanId); - - expect(childSpan?.attributes).toMatchObject({ - [EventStoreDBAttributes.EVENT_STORE_STREAM]: STREAM, - [EventStoreDBAttributes.EVENT_STORE_EVENT_ID]: event.id, - [EventStoreDBAttributes.EVENT_STORE_EVENT_TYPE]: event.type, - [EventStoreDBAttributes.EVENT_STORE_SUBSCRIPTION_ID]: subscription.id, - [EventStoreDBAttributes.SERVER_ADDRESS]: node.endpoints[0].address, - [EventStoreDBAttributes.SERVER_PORT]: node.endpoints[0].port.toString(), - [EventStoreDBAttributes.DATABASE_SYSTEM]: moduleName, - [EventStoreDBAttributes.DATABASE_OPERATION]: - "subscribeToPersistentSubscriptionToStream", - }); - - expect(parentSpan?.attributes).toMatchObject({ - [EventStoreDBAttributes.EVENT_STORE_STREAM]: STREAM, - [EventStoreDBAttributes.SERVER_ADDRESS]: node.endpoints[0].address, - [EventStoreDBAttributes.SERVER_PORT]: node.endpoints[0].port.toString(), - [EventStoreDBAttributes.DATABASE_SYSTEM]: moduleName, - [EventStoreDBAttributes.DATABASE_OPERATION]: "appendToStream", - }); - }); - - test("should create child span in persistent subscription to all", async () => { - const { - EventStoreDBClient, - jsonEvent, - persistentSubscriptionToAllSettingsFromDefaults, - START, - } = await import("@eventstore/db-client"); - - const GROUP = v4(); - const STREAM = v4(); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.createPersistentSubscriptionToAll( - GROUP, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { - filter: streamNameFilter({ - prefixes: [STREAM], - }), - } - ); - - const defer = new Defer(); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleEvent = jest.fn(async (event: ResolvedEvent) => { - if (event.event) { - await subscription.ack(event); - } - - if (event.event?.streamId == STREAM) { - defer.resolve(); - } - }); - const handleEnd = jest.fn(defer.resolve); - const onConfirmation = jest.fn(); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(GROUP, { - credentials: { - username: "admin", - password: "changeit", - }, - }) - .on("error", handleError) - .on("data", handleEvent) - .on("confirmation", onConfirmation) - .on("end", handleEnd); - - const event = jsonEvent({ - type: "SomeEvent", - data: {}, - }); - - await client.appendToStream(STREAM, event); - - await delay(500); - await defer.promise; - - const spans = memoryExporter.getFinishedSpans(); - - const parentSpan = spans.find( - (span) => span.name === EventStoreDBAttributes.STREAM_APPEND - ); - const childSpan = spans.find( - (span) => span.name === EventStoreDBAttributes.STREAM_SUBSCIBE - ); - - expect(parentSpan).toBeDefined(); - expect(childSpan).toBeDefined(); - expect(parentSpan?.spanContext().spanId).toBe(childSpan?.parentSpanId); - - expect(childSpan?.attributes).toMatchObject({ - [EventStoreDBAttributes.EVENT_STORE_STREAM]: STREAM, - [EventStoreDBAttributes.EVENT_STORE_EVENT_ID]: event.id, - [EventStoreDBAttributes.EVENT_STORE_EVENT_TYPE]: event.type, - [EventStoreDBAttributes.EVENT_STORE_SUBSCRIPTION_ID]: subscription.id, - [EventStoreDBAttributes.SERVER_ADDRESS]: node.endpoints[0].address, - [EventStoreDBAttributes.SERVER_PORT]: node.endpoints[0].port.toString(), - [EventStoreDBAttributes.DATABASE_SYSTEM]: moduleName, - [EventStoreDBAttributes.DATABASE_OPERATION]: - "subscribeToPersistentSubscriptionToAll", - [EventStoreDBAttributes.DATABASE_USER]: "admin", - }); - - expect(parentSpan?.attributes).toMatchObject({ - [EventStoreDBAttributes.EVENT_STORE_STREAM]: STREAM, - [EventStoreDBAttributes.SERVER_ADDRESS]: node.endpoints[0].address, - [EventStoreDBAttributes.SERVER_PORT]: node.endpoints[0].port.toString(), - [EventStoreDBAttributes.DATABASE_SYSTEM]: moduleName, - [EventStoreDBAttributes.DATABASE_OPERATION]: "appendToStream", - }); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/createPersistentSubscriptionToAll.test.ts b/packages/test/src/persistentSubscription/createPersistentSubscriptionToAll.test.ts deleted file mode 100644 index 3ba65d13..00000000 --- a/packages/test/src/persistentSubscription/createPersistentSubscriptionToAll.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - matchServerVersion, - createTestNode, - optionalDescribe, -} from "@test-utils"; - -import { - END, - EventStoreDBClient, - excludeSystemEvents, - PersistentSubscriptionExistsError, - persistentSubscriptionToAllSettingsFromDefaults, - START, - UnsupportedError, -} from "@eventstore/db-client"; - -describe("createPersistentSubscriptionToAll", () => { - const supported = matchServerVersion`>=21.10`; - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10)", () => { - test("Throws an unavailable error", async () => { - const GROUP_NAME = "oh_no"; - - try { - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - } catch (error) { - expect(error).toBeInstanceOf(UnsupportedError); - expect(error).toMatchInlineSnapshot( - `[Error: createPersistentSubscriptionToAll requires server version 21.10 or higher.]` - ); - } - }); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - describe("should create a persistent subscription to all", () => { - test("start from start", async () => { - const GROUP_NAME = "group_name_from_start"; - await expect( - client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }) - ) - ).resolves.toBeUndefined(); - }); - - test("start from end", async () => { - const GROUP_NAME = "group_name_from_end"; - await expect( - client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: END, - }) - ) - ).resolves.toBeUndefined(); - }); - - test("start from position", async () => { - const GROUP_NAME = "group_name_from_position"; - await expect( - client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: { commit: BigInt(1), prepare: BigInt(1) }, - }) - ) - ).resolves.toBeUndefined(); - }); - - test("with a filter", async () => { - const GROUP_NAME = "group_name_with_filter"; - await expect( - client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: END, - }), - { filter: excludeSystemEvents() } - ) - ).resolves.toBeUndefined(); - }); - }); - - test("should throw an error if group name exists", async () => { - const GROUP_NAME = "group_name_already_exists"; - - await expect( - client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ) - ).resolves.toBeUndefined(); - - await expect( - client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ) - ).rejects.toThrowError(PersistentSubscriptionExistsError); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/createPersistentSubscriptionToStream.test.ts b/packages/test/src/persistentSubscription/createPersistentSubscriptionToStream.test.ts deleted file mode 100644 index f8b3cd20..00000000 --- a/packages/test/src/persistentSubscription/createPersistentSubscriptionToStream.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { createTestNode } from "@test-utils"; - -import { - EventStoreDBClient, - PersistentSubscriptionExistsError, - persistentSubscriptionToStreamSettingsFromDefaults, - START, -} from "@eventstore/db-client"; - -describe("createPersistentSubscriptionToStream", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should create a persistent subscription", () => { - test("from start", async () => { - const STREAM_NAME = "stream_name_from_start"; - const GROUP_NAME = "group_name_from_start"; - await expect( - client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ) - ).resolves.toBeUndefined(); - }); - - test("from end", async () => { - const STREAM_NAME = "stream_name_from_end"; - const GROUP_NAME = "group_name_from_end"; - await expect( - client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() // end is default - ) - ).resolves.toBeUndefined(); - }); - - test("from revision", async () => { - const STREAM_NAME = "stream_name_from_revision"; - const GROUP_NAME = "group_name_from_revision"; - await expect( - client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: BigInt(1), - }) - ) - ).resolves.toBeUndefined(); - }); - }); - - test("should throw an error if subscription exists", async () => { - const STREAM_NAME = "stream_name_already_exists"; - const GROUP_NAME = "group_name_already_exists"; - - await expect( - client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ) - ).resolves.toBeUndefined(); - - await expect( - client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ) - ).rejects.toThrowError(PersistentSubscriptionExistsError); - }); -}); diff --git a/packages/test/src/persistentSubscription/deletePersistentSubscriptionToAll.test.ts b/packages/test/src/persistentSubscription/deletePersistentSubscriptionToAll.test.ts deleted file mode 100644 index ab41f76c..00000000 --- a/packages/test/src/persistentSubscription/deletePersistentSubscriptionToAll.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; - -import { - EventStoreDBClient, - persistentSubscriptionToAllSettingsFromDefaults, - UnsupportedError, -} from "@eventstore/db-client"; - -describe("deletePersistentSubscriptionToAll", () => { - const supported = matchServerVersion`>=21.10`; - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10)", () => { - test("Throws an unavailable error", async () => { - const GROUP_NAME = "oh_no"; - - try { - await client.deletePersistentSubscriptionToAll(GROUP_NAME); - } catch (error) { - expect(error).toBeInstanceOf(UnsupportedError); - expect(error).toMatchInlineSnapshot( - `[Error: deletePersistentSubscriptionToAll requires server version 21.10 or higher.]` - ); - } - }); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - test("should delete a persistent subscription", async () => { - const GROUP_NAME = "test_group_name"; - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - - await expect( - client.deletePersistentSubscriptionToAll(GROUP_NAME) - ).resolves.toBeUndefined(); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/deletePersistentSubscriptionToStream.test.ts b/packages/test/src/persistentSubscription/deletePersistentSubscriptionToStream.test.ts deleted file mode 100644 index b5424c59..00000000 --- a/packages/test/src/persistentSubscription/deletePersistentSubscriptionToStream.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { createTestNode } from "@test-utils"; - -import { - EventStoreDBClient, - persistentSubscriptionToStreamSettingsFromDefaults, -} from "@eventstore/db-client"; - -describe("deletePersistentSubscriptionToStream", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("should delete a persistent subscription", async () => { - const STREAM_NAME = "test_stream_name"; - const GROUP_NAME = "test_group_name"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ); - - await expect( - client.deletePersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - ).resolves.toBeUndefined(); - }); -}); diff --git a/packages/test/src/persistentSubscription/getPersistentSubscriptionToAllInfo.test.ts b/packages/test/src/persistentSubscription/getPersistentSubscriptionToAllInfo.test.ts deleted file mode 100644 index 448596c7..00000000 --- a/packages/test/src/persistentSubscription/getPersistentSubscriptionToAllInfo.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - delay, - jsonTestEvents, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; - -import { - AccessDeniedError, - END, - EventStoreDBClient, - PersistentSubscriptionDoesNotExistError, - persistentSubscriptionToAllSettingsFromDefaults, - Position, - ROUND_ROBIN, - START, - UnsupportedError, -} from "@eventstore/db-client"; - -describe("getPersistentSubscriptionToAllInfo", () => { - const supported = matchServerVersion`>=21.10.1`; - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10.1)", () => { - test("Throws an unavailable error", async () => { - const GROUP_NAME = "oh_no"; - - try { - await client.getPersistentSubscriptionToAllInfo(GROUP_NAME); - } catch (error) { - expect(error).toBeInstanceOf(UnsupportedError); - expect(error).toMatchInlineSnapshot( - `[Error: getPersistentSubscriptionToAllInfo requires server version 21.10.1 or higher.]` - ); - } - }); - }); - - optionalDescribe(supported)("Supported (>=21.10.1)", () => { - test("should get info on a persistent subscription to all", async () => { - const STREAM_NAME = "test_stream_name"; - const GROUP_NAME = "test_group_name"; - - const settings = persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: END, - checkPointUpperBound: 1, - }); - - await client.createPersistentSubscriptionToAll(GROUP_NAME, settings); - - const info = await client.getPersistentSubscriptionToAllInfo(GROUP_NAME); - - expect(info.groupName).toBe(GROUP_NAME); - expect(info.settings).toEqual(settings); - // We did not connect - expect(info.connections).toHaveLength(0); - - let position!: Position; - for await (const { event } of client.readAll({ maxCount: 60 })) { - if (!event) continue; - position = event.position; - } - - const settings2 = persistentSubscriptionToAllSettingsFromDefaults({ - ...settings, - startFrom: position, - checkPointLowerBound: 12, - maxSubscriberCount: 200, - consumerStrategyName: ROUND_ROBIN, - }); - - await client.updatePersistentSubscriptionToAll(GROUP_NAME, settings2); - - const info2 = await client.getPersistentSubscriptionToAllInfo(GROUP_NAME); - - expect(info2.eventSource).toBe("$all"); - expect(info2.groupName).toBe(GROUP_NAME); - expect(info2.settings).toEqual(settings2); - // We did not connect - expect(info2.connections).toHaveLength(0); - expect("lastKnownEventRevision" in info2.stats).toBe(false); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(500)); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME) - .on("error", jest.fn()) - .on("data", async (e) => { - await subscription.ack(e); - }); - - // let some events run through the subscription - await delay(1000); - await subscription.unsubscribe(); - await delay(1000); - - const info3 = await client.getPersistentSubscriptionToAllInfo(GROUP_NAME); - - expect(info3.eventSource).toBe("$all"); - expect(info3.groupName).toBe(GROUP_NAME); - expect(info3.settings).toEqual(settings2); - // We have disconnected - expect(info3.connections).toHaveLength(0); - - // These should not be populated - expect("lastKnownEventRevision" in info3.stats).toBe(false); - expect("lastCheckpointedEventRevision" in info3.stats).toBe(false); - - expect(typeof info3.stats.lastKnownEventPosition?.commit).toBe("bigint"); - expect(typeof info3.stats.lastKnownEventPosition?.prepare).toBe("bigint"); - - expect(typeof info3.stats.lastCheckpointedEventPosition?.commit).toBe( - "bigint" - ); - expect(typeof info3.stats.lastCheckpointedEventPosition?.prepare).toBe( - "bigint" - ); - }); - - test("should get info on a persistent subscription to all connection", async () => { - const STREAM_NAME = "test_stream_name_connection"; - const GROUP_NAME = "test_group_name_connection"; - - const settings = persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - extraStatistics: true, - }); - - await client.createPersistentSubscriptionToAll(GROUP_NAME, settings); - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME) - .on("error", jest.fn()) - .on("data", async (e) => { - await subscription.ack(e); - }); - - // let some events run through the subscription - await delay(1000); - - const info = await client.getPersistentSubscriptionToAllInfo(GROUP_NAME); - - await subscription.unsubscribe(); - - expect(info.eventSource).toBe("$all"); - expect(info.groupName).toBe(GROUP_NAME); - expect(info.settings).toEqual(settings); - - // We are the only connection - expect(info.connections).toHaveLength(1); - - const [connection] = info.connections; - - // We set our default user above - expect(connection.username).toBe("admin"); - // should be us connected - expect(connection.connectionName).toBe(client.connectionName); - - // we enabled extraStatistics. - expect(connection.extraStatistics).toBeDefined(); - expect(connection.extraStatistics!.get("quintile 3")).toBeDefined(); - }); - - describe("errors", () => { - test("PersistentSubscriptionDoesNotExist", async () => { - const GROUP_NAME = "does_not_exist_get_info_group_name"; - - try { - await client.getPersistentSubscriptionToAllInfo(GROUP_NAME); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(PersistentSubscriptionDoesNotExistError); - expect(error).toMatchInlineSnapshot( - `[Error: 5 NOT_FOUND: Subscription group does_not_exist_get_info_group_name on stream $all does not exist.]` - ); - - if (error instanceof PersistentSubscriptionDoesNotExistError) { - expect(error.groupName).toBe(GROUP_NAME); - } - } - }); - - test("AccessDenied", async () => { - const GROUP_NAME = "access_denied_get_info_group_name"; - - try { - await client.getPersistentSubscriptionToAllInfo(GROUP_NAME, { - credentials: { username: "AzureDiamond", password: "hunter2" }, - }); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/getPersistentSubscriptionToStreamInfo.test.ts b/packages/test/src/persistentSubscription/getPersistentSubscriptionToStreamInfo.test.ts deleted file mode 100644 index 2b84bcf9..00000000 --- a/packages/test/src/persistentSubscription/getPersistentSubscriptionToStreamInfo.test.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { createTestNode, delay, jsonTestEvents } from "@test-utils"; - -import { - AccessDeniedError, - END, - EventStoreDBClient, - PersistentSubscriptionDoesNotExistError, - persistentSubscriptionToStreamSettingsFromDefaults, - ROUND_ROBIN, - START, -} from "@eventstore/db-client"; - -describe("getPersistentSubscriptionToStreamInfo", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - connectionName: "getPersistentSubscriptionInfo test client", - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("should get info on a persistent subscription", async () => { - const STREAM_NAME = "test_stream_name"; - const GROUP_NAME = "test_group_name"; - - const settings = persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: END, - checkPointUpperBound: 1, - }); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - settings - ); - - const info = await client.getPersistentSubscriptionToStreamInfo( - STREAM_NAME, - GROUP_NAME - ); - - expect(info.eventSource).toBe(STREAM_NAME); - expect(info.groupName).toBe(GROUP_NAME); - expect(info.settings).toEqual(settings); - // We did not connect - expect(info.connections).toHaveLength(0); - - const settings2 = persistentSubscriptionToStreamSettingsFromDefaults({ - ...settings, - startFrom: BigInt(123), - checkPointLowerBound: 12, - maxSubscriberCount: 200, - consumerStrategyName: ROUND_ROBIN, - }); - - await client.updatePersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - settings2 - ); - - const info2 = await client.getPersistentSubscriptionToStreamInfo( - STREAM_NAME, - GROUP_NAME - ); - - expect(info2.eventSource).toBe(STREAM_NAME); - expect(info2.groupName).toBe(GROUP_NAME); - expect(info2.settings).toEqual(settings2); - // We did not connect - expect(info2.connections).toHaveLength(0); - expect(info2.stats.lastKnownEventRevision).toBeUndefined(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(500)); - - const subscription = client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - .on("error", jest.fn()) - .on("data", async (e) => { - await subscription.ack(e); - }); - - // let some events run through the subscription - await delay(1000); - await subscription.unsubscribe(); - await delay(1000); - - const info3 = await client.getPersistentSubscriptionToStreamInfo( - STREAM_NAME, - GROUP_NAME - ); - - expect(info3.eventSource).toBe(STREAM_NAME); - expect(info3.groupName).toBe(GROUP_NAME); - expect(info3.settings).toEqual(settings2); - // We have disconnected - expect(info3.connections).toHaveLength(0); - - // These should be populated as revisions - expect(typeof info3.stats.lastKnownEventRevision).toBe("bigint"); - expect(typeof info3.stats.lastCheckpointedEventRevision).toBe("bigint"); - }); - - test("should get info on a persistent subscription connection", async () => { - const STREAM_NAME = "test_stream_name_connection"; - const GROUP_NAME = "test_group_name_connection"; - - const settings = persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - extraStatistics: true, - }); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - settings - ); - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - const subscription = client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - .on("error", jest.fn()) - .on("data", async (e) => { - await subscription.ack(e); - }); - - // let some events run through the subscription - await delay(1000); - - const info = await client.getPersistentSubscriptionToStreamInfo( - STREAM_NAME, - GROUP_NAME - ); - - await subscription.unsubscribe(); - - expect(info.eventSource).toBe(STREAM_NAME); - expect(info.groupName).toBe(GROUP_NAME); - expect(info.settings).toEqual(settings); - - // We are the only connection - expect(info.connections).toHaveLength(1); - - const [connection] = info.connections; - - // We set our default user above - expect(connection.username).toBe("admin"); - // should be us connected - expect(connection.connectionName).toBe(client.connectionName); - - // we enabled extraStatistics. - expect(connection.extraStatistics).toBeDefined(); - expect(connection.extraStatistics!.get("quintile 3")).toBeDefined(); - }); - - describe("errors", () => { - test("PersistentSubscriptionDoesNotExist", async () => { - const STREAM_NAME = "does_not_exist_get_info_stream_name"; - const GROUP_NAME = "does_not_exist_get_info_group_name"; - - try { - await client.getPersistentSubscriptionToStreamInfo( - STREAM_NAME, - GROUP_NAME - ); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(PersistentSubscriptionDoesNotExistError); - expect(error).toMatchInlineSnapshot( - `[Error: 5 NOT_FOUND: Subscription group does_not_exist_get_info_group_name on stream does_not_exist_get_info_stream_name does not exist.]` - ); - - if (error instanceof PersistentSubscriptionDoesNotExistError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.groupName).toBe(GROUP_NAME); - } - } - }); - - test("AccessDenied", async () => { - const STREAM_NAME = "access_denied_get_info_stream_name"; - const GROUP_NAME = "access_denied_get_info_group_name"; - - try { - await client.getPersistentSubscriptionToStreamInfo( - STREAM_NAME, - GROUP_NAME, - { - credentials: { username: "AzureDiamond", password: "hunter2" }, - } - ); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/listAllPersistentSubscriptions.test.ts b/packages/test/src/persistentSubscription/listAllPersistentSubscriptions.test.ts deleted file mode 100644 index f5cb5f83..00000000 --- a/packages/test/src/persistentSubscription/listAllPersistentSubscriptions.test.ts +++ /dev/null @@ -1,213 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - CreatedPS, - CreatedPSToAll, - createManyPs, - createManyPsToAll, - createTestNode, - delay, - jsonTestEvents, - matchServerVersion, -} from "@test-utils"; - -import { - AccessDeniedError, - END, - EventStoreDBClient, - PersistentSubscriptionToAll, - PINNED, - Position, - ROUND_ROBIN, - START, -} from "@eventstore/db-client"; - -describe("listAllPersistentSubscriptions", () => { - const psToAllSupported = matchServerVersion`>=21.10.1`; - - const node = createTestNode(); - let client!: EventStoreDBClient; - const created: Array = []; - let psOfInterestToAll: CreatedPSToAll; - let psOfInterestToStream: CreatedPS; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - if (psToAllSupported) { - let position!: Position; - for await (const { event } of client.readAll({ maxCount: 60 })) { - if (!event) continue; - position = event.position; - } - - const createdAll = await createManyPsToAll(client)( - (i) => `test_group_name_to_all_${i}`, - [ - { - startFrom: START, - extraStatistics: true, - }, - { - startFrom: END, - }, - { - startFrom: position, - maxRetryCount: 12, - consumerStrategyName: PINNED, - }, - {}, - ] - ); - - created.push(...createdAll); - psOfInterestToAll = createdAll[0]; - } - - const createdSteam = await createManyPs(client)( - (i) => `test_stream_name_${i}`, - (i) => `test_group_name_${i}`, - [ - { - startFrom: START, - extraStatistics: true, - }, - { - startFrom: END, - }, - { - startFrom: BigInt(2), - maxRetryCount: 12, - consumerStrategyName: ROUND_ROBIN, - }, - {}, - ] - ); - created.push(...createdSteam); - psOfInterestToStream = createdSteam[0]; - }); - - afterAll(async () => { - await node.down(); - }); - - test("should list all persistent subscriptions", async () => { - const list = await client.listAllPersistentSubscriptions(); - - expect(list).toHaveLength(created.length); - - for (const [streamName, groupName, settings] of created) { - const ps = list.find((ps) => ps.groupName === groupName)!; - expect(ps).toBeDefined(); - expect(ps.eventSource).toBe(streamName); - expect(ps.groupName).toBe(groupName); - expect(ps.settings).toEqual(settings); - } - }); - - test("should list info on a persistent subscription connection", async () => { - const [streamNameOfInterest, groupNameOfInterest] = psOfInterestToStream; - const groupNameOfInterestToAll = psOfInterestToAll?.[1]; - - let subscriptionToAll: PersistentSubscriptionToAll | undefined; - if (psToAllSupported) { - subscriptionToAll = client - .subscribeToPersistentSubscriptionToAll(groupNameOfInterestToAll) - .on("data", async (e) => { - await subscriptionToAll!.ack(e); - }); - } - - await client.appendToStream(streamNameOfInterest, jsonTestEvents()); - - const subscriptionToStream = client - .subscribeToPersistentSubscriptionToStream( - streamNameOfInterest, - groupNameOfInterest - ) - .on("error", jest.fn()) - .on("data", async (e) => { - await subscriptionToStream.ack(e); - }); - - // let some events run through the subscriptions - await delay(1000); - - const list = await client.listAllPersistentSubscriptions(); - - expect(list).toHaveLength(created.length); - - for (const [streamName, groupName, settings] of created) { - const ps = list.find((ps) => ps.groupName === groupName)!; - expect(ps).toBeDefined(); - expect(ps.eventSource).toBe(streamName); - expect(ps.groupName).toBe(groupName); - expect(ps.settings).toEqual(settings); - - if ( - groupName === groupNameOfInterest || - (groupNameOfInterestToAll && groupName === groupNameOfInterestToAll) - ) { - // We are the only connection - expect(ps.connections).toHaveLength(1); - - const [connection] = ps.connections; - - // We set our default user above - expect(connection.username).toBe("admin"); - - // we enabled extraStatistics. - expect(connection.extraStatistics).toBeDefined(); - expect(connection.extraStatistics!.get("quintile 3")).toBeDefined(); - } - } - - await subscriptionToAll?.unsubscribe(); - await subscriptionToStream.unsubscribe(); - }); - - describe("errors", () => { - const emptyNode = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await emptyNode.up(); - - client = new EventStoreDBClient( - { - endpoint: emptyNode.uri, - }, - { rootCertificate: emptyNode.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await emptyNode.down(); - }); - - test("no persistent subscriptions", async () => { - const list = await client.listAllPersistentSubscriptions(); - expect(list).toHaveLength(0); - }); - - test("AccessDenied", async () => { - try { - await client.listAllPersistentSubscriptions({ - credentials: { username: "AzureDiamond", password: "hunter2" }, - }); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/listPersistentSubscriptionsToAll.test.ts b/packages/test/src/persistentSubscription/listPersistentSubscriptionsToAll.test.ts deleted file mode 100644 index 31997c73..00000000 --- a/packages/test/src/persistentSubscription/listPersistentSubscriptionsToAll.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - CreatedPSToAll, - createManyPsToAll, - createTestNode, - delay, - jsonTestEvents, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; - -import { - AccessDeniedError, - END, - EventStoreDBClient, - PersistentSubscriptionDoesNotExistError, - Position, - ROUND_ROBIN, - START, - UnsupportedError, -} from "@eventstore/db-client"; - -describe("listPersistentSubscriptionsToAll", () => { - const supported = matchServerVersion`>=21.10.1`; - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10.1)", () => { - test("Throws an unavailable error", async () => { - try { - await client.listPersistentSubscriptionsToAll(); - } catch (error) { - expect(error).toBeInstanceOf(UnsupportedError); - expect(error).toMatchInlineSnapshot( - `[Error: listPersistentSubscriptionsToAll requires server version 21.10.1 or higher.]` - ); - } - }); - }); - - optionalDescribe(supported)("Supported (>=21.10.1)", () => { - let created!: CreatedPSToAll[]; - - beforeAll(async () => { - let position!: Position; - for await (const { event } of client.readAll({ maxCount: 60 })) { - if (!event) continue; - position = event.position; - } - - created = await createManyPsToAll(client)( - (i) => `test_group_name_${i}`, - [ - { - startFrom: START, - extraStatistics: true, - }, - { - startFrom: END, - }, - { - startFrom: position, - maxRetryCount: 12, - consumerStrategyName: ROUND_ROBIN, - }, - {}, - ] - ); - }); - - test("should list persistent subscriptions on a stream", async () => { - const list = await client.listPersistentSubscriptionsToAll(); - - expect(list).toHaveLength(created.length); - - for (const [streamName, groupName, settings] of created) { - const ps = list.find((ps) => ps.groupName === groupName)!; - expect(ps).toBeDefined(); - expect(ps.eventSource).toBe(streamName); - expect(ps.groupName).toBe(groupName); - expect(ps.settings).toEqual(settings); - } - }); - - test("should list info on a persistent subscription connection", async () => { - const groupNameOfInterest = created[0][1]; - - await client.appendToStream("some_stream", jsonTestEvents()); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(groupNameOfInterest) - .on("data", async (e) => { - await subscription.ack(e); - }); - - // let some events run through the subscriptions - await delay(1000); - - const list = await client.listPersistentSubscriptionsToAll(); - - expect(list).toHaveLength(created.length); - - for (const [streamName, groupName, settings] of created) { - const ps = list.find((ps) => ps.groupName === groupName)!; - expect(ps).toBeDefined(); - expect(ps.eventSource).toBe(streamName); - expect(ps.groupName).toBe(groupName); - expect(ps.settings).toEqual(settings); - - if (groupName === groupNameOfInterest) { - // We are the only connection - expect(ps.connections).toHaveLength(1); - - const [connection] = ps.connections; - - // We set our default user above - expect(connection.username).toBe("admin"); - - // we enabled extraStatistics. - expect(connection.extraStatistics).toBeDefined(); - expect(connection.extraStatistics!.get("quintile 3")).toBeDefined(); - } - } - - await subscription.unsubscribe(); - }); - - describe("errors", () => { - const emptyNode = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await emptyNode.up(); - - client = new EventStoreDBClient( - { - endpoint: emptyNode.uri, - }, - { rootCertificate: emptyNode.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await emptyNode.down(); - }); - - test("PersistentSubscriptionDoesNotExist", async () => { - try { - await client.listPersistentSubscriptionsToAll(); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(PersistentSubscriptionDoesNotExistError); - expect(error).toMatchInlineSnapshot( - `[Error: 5 NOT_FOUND: Subscription group on stream $all does not exist.]` - ); - - if (error instanceof PersistentSubscriptionDoesNotExistError) { - expect(error.streamName).toBe("$all"); - } - } - }); - - test("AccessDenied", async () => { - try { - await client.listPersistentSubscriptionsToAll({ - credentials: { username: "AzureDiamond", password: "hunter2" }, - }); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/listPersistentSubscriptionsToStream.test.ts b/packages/test/src/persistentSubscription/listPersistentSubscriptionsToStream.test.ts deleted file mode 100644 index 4f4a0409..00000000 --- a/packages/test/src/persistentSubscription/listPersistentSubscriptionsToStream.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { - createManyPs, - createTestNode, - delay, - jsonTestEvents, -} from "@test-utils"; - -import { - AccessDeniedError, - END, - EventStoreDBClient, - PersistentSubscriptionDoesNotExistError, - ROUND_ROBIN, - START, -} from "@eventstore/db-client"; - -describe("listPersistentSubscriptions", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("should list persistent subscriptions on a stream", async () => { - const STREAM_NAME = "test_stream_name"; - const created = await createManyPs(client)( - () => STREAM_NAME, - (i) => `test_group_name_${i}`, - [ - { - startFrom: START, - extraStatistics: true, - }, - { - startFrom: END, - }, - { - startFrom: BigInt(2), - maxRetryCount: 12, - consumerStrategyName: ROUND_ROBIN, - }, - {}, - ] - ); - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - await delay(1000); - - const list = await client.listPersistentSubscriptionsToStream(STREAM_NAME); - - expect(list).toHaveLength(created.length); - - for (const [streamName, groupName, settings] of created) { - const ps = list.find((ps) => ps.groupName === groupName)!; - expect(ps).toBeDefined(); - expect(ps.eventSource).toBe(streamName); - expect(ps.groupName).toBe(groupName); - expect(ps.settings).toEqual(settings); - } - }); - - test("should list info on a persistent subscription connection", async () => { - const STREAM_NAME = "test_stream_name_connection"; - - const created = await createManyPs(client)( - () => STREAM_NAME, - (i) => `test_group_name_connection_${i}`, - [ - { - startFrom: START, - extraStatistics: true, - }, - { - startFrom: END, - }, - { - startFrom: BigInt(2), - maxRetryCount: 12, - }, - {}, - ] - ); - - const groupNameOfInterest = created[0][1]; - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - const subscription = client - .subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - groupNameOfInterest - ) - .on("error", jest.fn()) - .on("data", async (e) => { - await subscription.ack(e); - }); - - // let some events run through the subscription - await delay(1000); - - const list = await client.listPersistentSubscriptionsToStream(STREAM_NAME); - - expect(list).toHaveLength(created.length); - - for (const [streamName, groupName, settings] of created) { - const ps = list.find((ps) => ps.groupName === groupName)!; - expect(ps).toBeDefined(); - expect(ps.eventSource).toBe(streamName); - expect(ps.groupName).toBe(groupName); - expect(ps.settings).toEqual(settings); - - if (groupName === groupNameOfInterest) { - // We are the only connection - expect(ps.connections).toHaveLength(1); - - const [connection] = ps.connections; - - // We set our default user above - expect(connection.username).toBe("admin"); - - // we enabled extraStatistics. - expect(connection.extraStatistics).toBeDefined(); - expect(connection.extraStatistics!.get("quintile 3")).toBeDefined(); - } - } - - await subscription.unsubscribe(); - }); - - describe("errors", () => { - test("PersistentSubscriptionDoesNotExist", async () => { - const STREAM_NAME = "does_not_exist_list_stream_name"; - - try { - await client.listPersistentSubscriptionsToStream(STREAM_NAME); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(PersistentSubscriptionDoesNotExistError); - expect(error).toMatchInlineSnapshot( - `[Error: 5 NOT_FOUND: Subscription group on stream does_not_exist_list_stream_name does not exist.]` - ); - - if (error instanceof PersistentSubscriptionDoesNotExistError) { - expect(error.streamName).toBe(STREAM_NAME); - } - } - }); - - test("AccessDenied", async () => { - const STREAM_NAME = "access_denied_list_stream_name"; - - try { - await client.listPersistentSubscriptionsToStream(STREAM_NAME, { - credentials: { username: "AzureDiamond", password: "hunter2" }, - }); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/replayParkedMessagesToAll.test.ts b/packages/test/src/persistentSubscription/replayParkedMessagesToAll.test.ts deleted file mode 100644 index 0efdb2fa..00000000 --- a/packages/test/src/persistentSubscription/replayParkedMessagesToAll.test.ts +++ /dev/null @@ -1,287 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestCluster, - delay, - jsonTestEvents, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; - -import { - AccessDeniedError, - EventStoreDBClient, - jsonEvent, - PARK, - PersistentSubscriptionDoesNotExistError, - persistentSubscriptionToAllSettingsFromDefaults, - START, - streamNameFilter, - UnsupportedError, -} from "@eventstore/db-client"; - -describe("replayParkedMessagesToAll", () => { - const supported = matchServerVersion`>=21.10.1`; - const cluster = createTestCluster(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await cluster.up(); - - client = new EventStoreDBClient( - { endpoints: cluster.endpoints, nodePreference: "leader" }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await cluster.down(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10.1)", () => { - test("Throws an unavailable error", async () => { - const GROUP_NAME = "oh_no"; - - try { - await client.replayParkedMessagesToAll(GROUP_NAME); - } catch (error) { - expect(error).toBeInstanceOf(UnsupportedError); - expect(error).toMatchInlineSnapshot( - `[Error: replayParkedMessagesToAll requires server version 21.10.1 or higher.]` - ); - } - }); - }); - - optionalDescribe(supported)("Supported (>=21.10.1)", () => { - test("should replay all parked messages by default", async () => { - const GROUP_NAME = "replay_to_end_parked_group_name"; - const PREFIX = "replay_parked_to_all_"; - const FINISH = "finish-test"; - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { - filter: streamNameFilter({ prefixes: [PREFIX] }), - } - ); - - await client.appendToStream(`${PREFIX}something`, [ - ...jsonTestEvents(19), - jsonEvent({ - type: FINISH, - data: {}, - }), - ]); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - const parkEvent = jest.fn((resolvedEvent) => - subscription.nack(PARK, "Park it", resolvedEvent) - ); - const ackEvent = jest.fn((resolvedEvent) => - subscription.ack(resolvedEvent) - ); - - let hasReplayed = false; - for await (const resolvedEvent of subscription) { - if (!hasReplayed) { - await parkEvent(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH) { - await delay(5000); // wait for all nacks to be processed - await client.replayParkedMessagesToAll(GROUP_NAME); - hasReplayed = true; - } - - continue; - } - - await ackEvent(resolvedEvent); - if (resolvedEvent.event?.type === FINISH) break; - } - - expect(parkEvent).toHaveBeenCalledTimes(20); - expect(ackEvent).toHaveBeenCalledTimes(20); - }); - - test("should stop at requested stopAt", async () => { - const GROUP_NAME = "replay_to_stop_parked_group_name"; - const PREFIX = "replay_to_stop_parked_to_all_"; - const FINISH = "finish-test"; - const STOP_AT = 4; - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { - filter: streamNameFilter({ prefixes: [PREFIX] }), - } - ); - - await client.appendToStream(`${PREFIX}something`, [ - ...jsonTestEvents(19), - jsonEvent({ - type: FINISH, - data: {}, - }), - ]); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - const parkEvent = jest.fn((resolvedEvent) => - subscription.nack(PARK, "Park it", resolvedEvent) - ); - const ackEvent = jest.fn((resolvedEvent) => - subscription.ack(resolvedEvent) - ); - - let hasReplayed = false; - let count = 0; - for await (const resolvedEvent of subscription) { - if (!hasReplayed) { - await parkEvent(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH) { - await delay(5000); // wait for all nacks to be processed - await client.replayParkedMessagesToAll(GROUP_NAME, { - stopAt: STOP_AT, - }); - hasReplayed = true; - } - - continue; - } - - await ackEvent(resolvedEvent); - count++; - - if (count === STOP_AT) { - // wait 5 seconds, then unsubscribe. - // the subscription will continue to recieve events in the meantime. - delay(5000).then(() => subscription.unsubscribe()); - } - - if (resolvedEvent.event?.type === FINISH) { - // we should never get this far. - break; - } - } - - expect(parkEvent).toHaveBeenCalledTimes(20); - expect(ackEvent).toHaveBeenCalledTimes(STOP_AT); - }); - - test("uri encoding", async () => { - const PREFIX = - "어학연구소/0️⃣/ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็_"; - const GROUP_NAME = "ヽ༼ຈل͜ຈ༽ノ / ヽ༼ຈل͜ຈ༽ノ"; - const FINISH = "finish-test"; - const STOP_AT = 4; - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { - filter: streamNameFilter({ prefixes: [PREFIX] }), - } - ); - - await client.appendToStream(`${PREFIX}something`, [ - ...jsonTestEvents(19), - jsonEvent({ - type: FINISH, - data: {}, - }), - ]); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - const parkEvent = jest.fn((resolvedEvent) => - subscription.nack(PARK, "Park it", resolvedEvent) - ); - const ackEvent = jest.fn((resolvedEvent) => - subscription.ack(resolvedEvent) - ); - - let hasReplayed = false; - let count = 0; - for await (const resolvedEvent of subscription) { - if (!hasReplayed) { - await parkEvent(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH) { - await delay(5000); // wait for all nacks to be processed - await client.replayParkedMessagesToAll(GROUP_NAME, { - stopAt: STOP_AT, - }); - hasReplayed = true; - } - - continue; - } - - await ackEvent(resolvedEvent); - count++; - - if (count === STOP_AT) { - // wait 5 seconds, then unsubscribe. - // the subscription will continue to recieve events in the meantime. - delay(5000).then(() => subscription.unsubscribe()); - } - - if (resolvedEvent.event?.type === FINISH) { - // we should never get this far. - break; - } - } - - expect(parkEvent).toHaveBeenCalledTimes(20); - expect(ackEvent).toHaveBeenCalledTimes(STOP_AT); - }); - - describe("errors", () => { - test("PersistentSubscriptionDoesNotExist", async () => { - const GROUP_NAME = "does_not_exist_replay_parked_group_name"; - - try { - await client.replayParkedMessagesToAll(GROUP_NAME); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(PersistentSubscriptionDoesNotExistError); - expect(error).toMatchInlineSnapshot( - `[Error: 5 NOT_FOUND: Subscription group does_not_exist_replay_parked_group_name on stream $all does not exist.]` - ); - - if (error instanceof PersistentSubscriptionDoesNotExistError) { - expect(error.groupName).toBe(GROUP_NAME); - } - } - }); - - test("AccessDenied", async () => { - const GROUP_NAME = "access_denied_replay_parked_group_name"; - - try { - await client.replayParkedMessagesToAll(GROUP_NAME, { - credentials: { username: "AzureDiamond", password: "hunter2" }, - }); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/replayParkedMessagesToStream.test.ts b/packages/test/src/persistentSubscription/replayParkedMessagesToStream.test.ts deleted file mode 100644 index 2b5525ef..00000000 --- a/packages/test/src/persistentSubscription/replayParkedMessagesToStream.test.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { createTestCluster, delay, jsonTestEvents } from "@test-utils"; - -import { - AccessDeniedError, - EventStoreDBClient, - jsonEvent, - PARK, - PersistentSubscriptionDoesNotExistError, - persistentSubscriptionToStreamSettingsFromDefaults, - START, -} from "@eventstore/db-client"; - -describe("replayParkedMessagesToStream", () => { - const cluster = createTestCluster(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await cluster.up(); - - client = new EventStoreDBClient( - { endpoints: cluster.endpoints, nodePreference: "leader" }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await cluster.down(); - }); - - test("should replay all parked messages by default", async () => { - const STREAM_NAME = "replay_to_end_parked_stream_name"; - const GROUP_NAME = "replay_to_end_parked_group_name"; - const FINISH = "finish-test"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(19), - jsonEvent({ - type: FINISH, - data: {}, - }), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - const parkEvent = jest.fn((resolvedEvent) => - subscription.nack(PARK, "Park it", resolvedEvent) - ); - const ackEvent = jest.fn((resolvedEvent) => - subscription.ack(resolvedEvent) - ); - - let hasReplayed = false; - for await (const resolvedEvent of subscription) { - if (!hasReplayed) { - await parkEvent(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH) { - await delay(5000); // wait for all nacks to be processed - await client.replayParkedMessagesToStream(STREAM_NAME, GROUP_NAME); - hasReplayed = true; - } - - continue; - } - - await ackEvent(resolvedEvent); - if (resolvedEvent.event?.type === FINISH) break; - } - - expect(parkEvent).toHaveBeenCalledTimes(20); - expect(ackEvent).toHaveBeenCalledTimes(20); - }); - - test("should stop at requested stopAt", async () => { - const STREAM_NAME = "replay_to_stop_parked_stream_name"; - const GROUP_NAME = "replay_to_stop_parked_group_name"; - const FINISH = "finish-test"; - const STOP_AT = 4; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(19), - jsonEvent({ - type: FINISH, - data: {}, - }), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - const parkEvent = jest.fn((resolvedEvent) => - subscription.nack(PARK, "Park it", resolvedEvent) - ); - const ackEvent = jest.fn((resolvedEvent) => - subscription.ack(resolvedEvent) - ); - - let hasReplayed = false; - let count = 0; - for await (const resolvedEvent of subscription) { - if (!hasReplayed) { - await parkEvent(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH) { - await delay(5000); // wait for all nacks to be processed - await client.replayParkedMessagesToStream(STREAM_NAME, GROUP_NAME, { - stopAt: STOP_AT, - }); - hasReplayed = true; - } - - continue; - } - - await ackEvent(resolvedEvent); - count++; - - if (count === STOP_AT) { - // wait 5 seconds, then unsubscribe. - // the subscription will continue to recieve events in the meantime. - delay(5000).then(() => subscription.unsubscribe()); - } - - if (resolvedEvent.event?.type === FINISH) { - // we should never get this far. - break; - } - } - - expect(parkEvent).toHaveBeenCalledTimes(20); - expect(ackEvent).toHaveBeenCalledTimes(STOP_AT); - }); - - test("uri encoding", async () => { - const STREAM_NAME = - "어학연구소/0️⃣/ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็"; - const GROUP_NAME = "ヽ༼ຈل͜ຈ༽ノ ヽ༼ຈل͜ຈ༽ノ"; - const FINISH = "finish-test"; - const STOP_AT = 4; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(19), - jsonEvent({ - type: FINISH, - data: {}, - }), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - const parkEvent = jest.fn((resolvedEvent) => - subscription.nack(PARK, "Park it", resolvedEvent) - ); - const ackEvent = jest.fn((resolvedEvent) => - subscription.ack(resolvedEvent) - ); - - let hasReplayed = false; - let count = 0; - for await (const resolvedEvent of subscription) { - if (!hasReplayed) { - await parkEvent(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH) { - await delay(5000); // wait for all nacks to be processed - await client.replayParkedMessagesToStream(STREAM_NAME, GROUP_NAME, { - stopAt: STOP_AT, - }); - hasReplayed = true; - } - - continue; - } - - await ackEvent(resolvedEvent); - count++; - - if (count === STOP_AT) { - // wait 5 seconds, then unsubscribe. - // the subscription will continue to recieve events in the meantime. - delay(5000).then(() => subscription.unsubscribe()); - } - - if (resolvedEvent.event?.type === FINISH) { - // we should never get this far. - break; - } - } - - expect(parkEvent).toHaveBeenCalledTimes(20); - expect(ackEvent).toHaveBeenCalledTimes(STOP_AT); - }); - - describe("errors", () => { - test("PersistentSubscriptionDoesNotExist", async () => { - const STREAM_NAME = "does_not_exist_replay_parked_stream_name"; - const GROUP_NAME = "does_not_exist_replay_parked_group_name"; - - try { - await client.replayParkedMessagesToStream(STREAM_NAME, GROUP_NAME); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(PersistentSubscriptionDoesNotExistError); - expect(error).toMatchInlineSnapshot( - `[Error: 5 NOT_FOUND: Subscription group does_not_exist_replay_parked_group_name on stream does_not_exist_replay_parked_stream_name does not exist.]` - ); - - if (error instanceof PersistentSubscriptionDoesNotExistError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.groupName).toBe(GROUP_NAME); - } - } - }); - - test("AccessDenied", async () => { - const STREAM_NAME = "access_denied_replay_parked_stream_name"; - const GROUP_NAME = "access_denied_replay_parked_group_name"; - - try { - await client.replayParkedMessagesToStream(STREAM_NAME, GROUP_NAME, { - credentials: { username: "AzureDiamond", password: "hunter2" }, - }); - throw "unreachable"; - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/restartPersistentSubscriptionSubsystem.test.ts b/packages/test/src/persistentSubscription/restartPersistentSubscriptionSubsystem.test.ts deleted file mode 100644 index e4f6cb11..00000000 --- a/packages/test/src/persistentSubscription/restartPersistentSubscriptionSubsystem.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createTestNode } from "@test-utils"; - -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("restartPersistentSubscriptionSubsystem", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("Doesnt error", async () => { - await expect( - client.restartPersistentSubscriptionSubsystem() - ).resolves.toBeUndefined(); - }); -}); diff --git a/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll-filters.test.ts b/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll-filters.test.ts deleted file mode 100644 index ce54c505..00000000 --- a/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll-filters.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestCluster, - jsonTestEvents, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; - -import { - EventStoreDBClient, - jsonEvent, - persistentSubscriptionToAllSettingsFromDefaults, - START, - streamNameFilter, - END, - excludeSystemEvents, - eventTypeFilter, -} from "@eventstore/db-client"; - -describe("subscribeToPersistentSubscriptionToAll (filters)", () => { - const supported = matchServerVersion`>=21.10`; - const cluster = createTestCluster(); - let client!: EventStoreDBClient; - - const finishEvent = (type: string) => - jsonEvent({ - type, - data: { - message: "lets wrap this up", - }, - }); - - beforeAll(async () => { - await cluster.up(); - - client = new EventStoreDBClient( - { endpoints: cluster.endpoints, nodePreference: "leader" }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await cluster.down(); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - describe("streamName", () => { - test.each` - name | filter | streamName - ${"prefixes"} | ${streamNameFilter({ prefixes: ["prefix_filter_streamname"] })} | ${(k: string) => `prefix_filter_streamname_${k}`} - ${"regex"} | ${streamNameFilter({ regex: "^[0-9]*_regex_filter_streamname_" })} | ${(k: string) => `${Math.floor(Math.random() * 1000)}_regex_filter_streamname_${k}`} - `("$name", async ({ name, filter, streamName }) => { - const STREAM_NAME_A = streamName("a"); - const STREAM_NAME_B = streamName("b"); - - const GROUP_NAME = `streamName_filter_${name}_test`; - const FINISH_TEST = `finish_streamName_filter_${name}_test`; - const doSomething = jest.fn(); - - await client.appendToStream(STREAM_NAME_A, jsonTestEvents(8)); - await client.appendToStream(STREAM_NAME_B, jsonTestEvents(8)); - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(8), - finishEvent(FINISH_TEST), - ]); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { filter } - ); - - const ps$all = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const event of ps$all) { - doSomething(event); - await ps$all.ack(event); - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething).toBeCalledTimes( - 8 + // a - 8 + // b - 8 + // a - 1 // finish - ); - }); - }); - - describe("eventType", () => { - test.each` - name | filter | eventType - ${"prefixes"} | ${eventTypeFilter({ prefixes: ["prefix_filter_eventType"] })} | ${(k: string) => `prefix_filter_eventType_${k}`} - ${"regex"} | ${eventTypeFilter({ regex: "^[0-9]*_regex_filter_eventType_[A-z]*$" })} | ${(k: string) => `${Math.floor(Math.random() * 1000)}_regex_filter_eventType_${k}`} - `("$name", async ({ name, filter, eventType }) => { - const STREAM_NAME_A = `filter_eventType_${name}_a`; - const STREAM_NAME_B = `filter_eventType_${name}_b`; - - const GROUP_NAME = `eventType_filter_${name}_test`; - const FINISH_TEST = eventType("finish"); - const doSomething = jest.fn(); - - await client.appendToStream( - STREAM_NAME_A, - jsonTestEvents(8, eventType("a")) - ); - await client.appendToStream( - STREAM_NAME_B, - jsonTestEvents(8, eventType("b")) - ); - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(8, eventType("c")), - finishEvent(FINISH_TEST), - ]); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { filter } - ); - - const ps$all = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const event of ps$all) { - doSomething(event); - - await ps$all.ack(event); - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething).toBeCalledTimes( - 8 + // a - 8 + // b - 8 + // a - 1 // finish - ); - }); - }); - - test("excludeSystemEvents", async () => { - const GROUP_NAME = "exclude_system_events"; - const STREAM_NAME = "exclude_system_events_stream"; - const FINISH_TEST = "finish_exclude_system_events"; - const doSomething = jest.fn(); - const doSomethingWithNonSystemEvent = jest.fn(); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: END, - }), - { filter: excludeSystemEvents() } - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(8), - finishEvent(FINISH_TEST), - ]); - - const ps$all = client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const event of ps$all) { - doSomething(event); - - if (!event.event?.type.startsWith("$")) { - doSomethingWithNonSystemEvent(event); - } - - await ps$all.ack(event); - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - // We run from the start, so could be more - expect(doSomething.mock.calls.length).toBeGreaterThanOrEqual(9); - expect(doSomethingWithNonSystemEvent).toBeCalledTimes( - doSomething.mock.calls.length - ); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll.test.ts b/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll.test.ts deleted file mode 100644 index f39a0648..00000000 --- a/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToAll.test.ts +++ /dev/null @@ -1,765 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { pipeline, Writable, Readable, Transform } from "stream"; -import { promisify } from "util"; - -import { - createTestCluster, - Defer, - delay, - jsonTestEvents, - matchServerVersion, - optionalDescribe, - postEventViaHttpApi, -} from "@test-utils"; - -import { - AllStreamResolvedEvent, - NotLeaderError, - PersistentSubscriptionToStream, - EventStoreDBClient, - jsonEvent, - persistentSubscriptionToAllSettingsFromDefaults, - START, - UnsupportedError, - streamNameFilter, - END, -} from "@eventstore/db-client"; - -const asyncPipeline = promisify(pipeline); - -describe("subscribeToPersistentSubscriptionToAll", () => { - const supported = matchServerVersion`>=21.10`; - const cluster = createTestCluster(); - let client!: EventStoreDBClient; - - const finishEvent = (type: string) => - jsonEvent({ - type, - data: { - message: "lets wrap this up", - }, - }); - - beforeAll(async () => { - await cluster.up(); - - client = new EventStoreDBClient( - { endpoints: cluster.endpoints, nodePreference: "leader" }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await cluster.down(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10)", () => { - test("Throws an unavailable error", async () => { - const GROUP_NAME = "oh_no"; - - try { - const ps$all = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const event of ps$all) { - expect(event).toBe("unreachable"); - } - } catch (error) { - expect(error).toBeInstanceOf(UnsupportedError); - expect(error).toMatchInlineSnapshot( - `[Error: subscribeToPersistentSubscriptionToAll requires server version 21.10 or higher.]` - ); - } - }); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - describe("should connect to a persistent subscription to all", () => { - test("from start", async () => { - const STREAM_PREFIX = "connect_from_start_"; - const GROUP_NAME = "from_start_test_group_name"; - const FINISH_TEST = "from_start_test_finish_test"; - - await client.appendToStream(`${STREAM_PREFIX}a`, jsonTestEvents(20)); - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }), - { filter: streamNameFilter({ prefixes: [STREAM_PREFIX] }) } - ); - - const defer = new Defer(); - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: AllStreamResolvedEvent) => { - if (event.event) { - await subscription.ack(event); - } - - if (event.event?.type === FINISH_TEST) { - defer.resolve(); - } - }); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await client.appendToStream(`${STREAM_PREFIX}b`, [ - ...jsonTestEvents(3), - finishEvent(FINISH_TEST), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - expect(onEvent).toBeCalled(); - }); - - test("from position", async () => { - const GROUP_NAME = "from_position_test_group_name"; - const FINISH_TEST = "from_position_test_finish_test"; - - const { position } = await client.appendToStream( - "some_stream", - jsonTestEvents(1) - ); - - await client.appendToStream("some_stream", jsonTestEvents(20)); - - expect(position).toBeDefined(); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: position, - }), - {} - ); - - const defer = new Defer(); - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: AllStreamResolvedEvent) => { - await subscription.ack(event); - - if (event.event?.type === FINISH_TEST) { - defer.resolve(); - } - }); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await client.appendToStream("a_stream", [ - ...jsonTestEvents(3), - finishEvent(FINISH_TEST), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - // 20 pre-write + 4 after + system events - expect(onEvent.mock.calls.length).toBeGreaterThanOrEqual(24); - }); - - test("from end", async () => { - const STREAM_NAME = "from_end_test_stream_name"; - const GROUP_NAME = "from_end_test_group_name"; - const FINISH_TEST = "from_end_test_finish_test"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() // end is default - ); - - const defer = new Defer(); - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: AllStreamResolvedEvent) => { - await subscription.ack(event); - - if (event.event?.type === FINISH_TEST) { - defer.resolve(); - } - }); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3), - finishEvent(FINISH_TEST), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - // 4 after + system events - expect(onEvent.mock.calls.length).toBeGreaterThanOrEqual(4); - }); - - test("nack", async () => { - const STREAM_NAME = "nack_test_stream_name"; - const GROUP_NAME = "nack_test_group_name"; - const FINISH_TEST = "nack_test_finish_event"; - - // Skip the first twenty events and retry the next 20 events. - // we should see the number of times that the `onEvent` callback - // is called reflects this (if nack is working) - - const skipCount = 20; - const retryCount = 20; - - const { position } = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(1, "mark") - ); - - expect(position).toBeDefined(); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(skipCount, "skip-event"), - ...jsonTestEvents(retryCount, "retry-event"), - finishEvent(FINISH_TEST), - ]); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: position, - }) - ); - - const defer = new Defer(); - - const nacked: string[] = []; - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: AllStreamResolvedEvent) => { - if (!event.event) return; - - if (event.event.type === FINISH_TEST) { - await subscription.ack(event); - defer.resolve(); - return; - } - - if ( - !event.event.streamId.startsWith("$") && - !nacked.includes(event.event.id) - ) { - nacked.push(event.event.id); - - await subscription.nack( - event.event.type === "skip-event" ? "skip" : "retry", - "To test it", - event - ); - return; - } - - await subscription.ack(event); - return; - }); - - const subscription = client - .subscribeToPersistentSubscriptionToAll(GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - // mark + skipped + retried - expect(nacked.length).toBe(1 + skipCount + retryCount); - // mark + skipped + retried (twice) + finish test + system events - expect(onEvent.mock.calls.length).toBeGreaterThanOrEqual( - 1 + skipCount + retryCount * 2 + 1 - ); - }); - }); - - describe("should return a readable stream", () => { - describe("async iterator", () => { - test("ack", async () => { - const STREAM_NAME = "async_iter_ack_stream"; - const GROUP_NAME = "async_iter_ack_group_name"; - const FINISH_TEST = "async_iter_ack_finish_test"; - const doSomething = jest.fn(); - - const { position } = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(1, "mark") - ); - - expect(position).toBeDefined(); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(FINISH_TEST), - ]); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: position, - }) - ); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const resolvedEvent of subscription) { - doSomething(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH_TEST) { - break; - } - } - - // 100 events + system events - expect(doSomething.mock.calls.length).toBeGreaterThanOrEqual(100); - }); - - test("nack", async () => { - const STREAM_NAME = "async_iter_nack"; - const GROUP_NAME = "async_iter_nack_group_name"; - const FINISH_TEST = "async_iter_nack_finish_test"; - const doSomething = jest.fn(); - const nacked: string[] = []; - - const { position } = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(1, "mark") - ); - - expect(position).toBeDefined(); - - // Skip the first twenty events and retry the next 20 events. - // we should see the number of times that the `onEvent` callback - // is called reflects this (if nack is working) - - const skipCount = 20; - const retryCount = 20; - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(skipCount, "skip-event"), - ...jsonTestEvents(retryCount, "retry-event"), - finishEvent(FINISH_TEST), - ]); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: position, - }) - ); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const resolvedEvent of subscription) { - if (!resolvedEvent.event) continue; - - doSomething(resolvedEvent); - - if (resolvedEvent.event.type === FINISH_TEST) { - await subscription.ack(resolvedEvent); - break; - } - - if ( - !resolvedEvent.event.streamId.startsWith("$") && - !nacked.includes(resolvedEvent.event.id) - ) { - nacked.push(resolvedEvent.event.id); - await subscription.nack( - resolvedEvent.event.type === "skip-event" ? "skip" : "retry", - "To test it", - resolvedEvent - ); - continue; - } - - await subscription.ack(resolvedEvent); - } - - // mark + skipped + retried - expect(nacked.length).toBe(1 + skipCount + retryCount); - // mark + skipped + retried (twice) + finish test + system events - expect(doSomething.mock.calls.length).toBeGreaterThanOrEqual( - 1 + skipCount + retryCount * 2 + 1 - ); - }); - - test("ack with async function", async () => { - const STREAM_NAME = "async_iter_ack_fun"; - const GROUP_NAME = "async_iter_ack_fun_group_name"; - const FINISH_TEST = "async_iter_ack_fun_finish_test"; - const doSomething = jest.fn(); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(FINISH_TEST), - ]); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const resolvedEvent of subscription) { - const { event } = resolvedEvent; - if (!event) continue; - - if (event.type === "test") { - // example of awaiting an async function when iterating over the async iterator - await delay(10); - } - - doSomething(event); - - await subscription.ack(resolvedEvent); - - if (event.type === FINISH_TEST) { - break; - } - } - - // 100 events + system events - expect(doSomething.mock.calls.length).toBeGreaterThanOrEqual(100); - }); - }); - - test("after the fact event listeners", async () => { - const STREAM_NAME = "after_the_fact"; - const GROUP_NAME = "after_the_fact_group_name"; - const FINISH_TEST = "after_the_fact_finish_test"; - - const defer = new Defer(); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: END, - }) - ); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - const eventListenerOne = jest.fn(); - const eventListenerTwo = jest.fn(); - const onError = jest.fn(); - const endListener = jest.fn(() => { - defer.resolve(); - }); - - subscription - .on("data", eventListenerOne) - .on("data", async (resolvedEvent) => { - eventListenerTwo(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH_TEST) { - subscription.unsubscribe(); - } - }) - .on("error", onError) - .on("end", endListener); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(5), - finishEvent(FINISH_TEST), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(eventListenerOne.mock.calls.length).toBeGreaterThan(6); - expect(eventListenerTwo.mock.calls.length).toBeGreaterThan(6); - }); - - test("pipeline", async () => { - const STREAM_NAME = "pipeline_test"; - const GROUP_NAME = "pipeline_test_group_name"; - const FINISH_TEST = "finish_pipeline"; - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(8), - jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }), - ]); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - const acker = new (class extends Transform { - _transform( - resolvedEvent: AllStreamResolvedEvent, - _encoding: string, - done: (error: null, e: AllStreamResolvedEvent) => void - ) { - subscription - .ack(resolvedEvent) - .then(() => done(null, resolvedEvent)); - } - })({ objectMode: true }); - - const writeStream = new (class extends Writable { - public ids: string[] = []; - _write( - { event }: AllStreamResolvedEvent, - _encoding: string, - done: () => void - ) { - if (!event?.streamId.startsWith("$")) { - this.ids.push(event!.id); - } - - if (event?.type === FINISH_TEST) { - subscription.unsubscribe().then(done); - } else { - done(); - } - } - })({ objectMode: true }); - - await asyncPipeline(subscription as Readable, acker, writeStream); - - expect(writeStream.ids).toHaveLength(9); - }); - }); - - test("malformed events", async () => { - const STREAM_NAME = "malformed_json"; - const GROUP_NAME = "malformed_json_group_name"; - const FINISH_TEST = "malformed_json_finish_test"; - const doSomething = jest.fn(); - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: END, - }) - ); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(3, "test 1")); - - const malformedData = "****"; - - await postEventViaHttpApi(cluster, { - contentType: "application/json", - type: "malformed-event", - stream: STREAM_NAME, - data: malformedData, - }); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3, "test 2"), - finishEvent(FINISH_TEST), - ]); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - for await (const resolvedEvent of subscription) { - doSomething(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === "malformed-event") { - expect(resolvedEvent.event.data).toBe(malformedData); - } - - if (resolvedEvent.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething).toBeCalled(); - }); - - test("should throw on follower node", async () => { - // Create connection to a follower node - const followerClient = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - nodePreference: "follower", - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - const STREAM_NAME = "follower_node_test"; - const GROUP_NAME = "follower_node_test"; - const FINISH_TEST = "follower_node_finish_test"; - const doSomething = jest.fn(); - const confirmThatErrorWasThrown = jest.fn(); - - const createAndConnectWithAutoReconnect = async ( - client: EventStoreDBClient - ): Promise => { - try { - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: START, - }) - ); - - return client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - } catch (error) { - confirmThatErrorWasThrown(error); - - // Our command is good, but must be executed on the leader - if (error instanceof NotLeaderError) { - // Create new client connected to the reported leader node - const leaderClient = new EventStoreDBClient( - { - endpoint: error.leader, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - // try again with new connection - return createAndConnectWithAutoReconnect(leaderClient); - } - - // other errors can be passed up the chain - throw error; - } - }; - - await followerClient.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(FINISH_TEST), - ]); - - const subscription = await createAndConnectWithAutoReconnect( - followerClient - ); - - for await (const resolvedEvent of subscription) { - doSomething(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething.mock.calls.length).toBeGreaterThanOrEqual(100); - - expect(confirmThatErrorWasThrown).toBeCalledTimes(1); - expect(confirmThatErrorWasThrown.mock.calls[0][0]).toBeInstanceOf( - NotLeaderError - ); - }); - - test("retryCount", async () => { - const STREAM_NAME = "to_all_retry_count_stream_name"; - const GROUP_NAME = "to_all_retry_count_group_name"; - const FINISH_TEST = "to_all_retry_count_finish_test"; - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults({ - startFrom: END, - maxRetryCount: 5, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(FINISH_TEST), - ]); - - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - const nacked: Record = {}; - - for await (const resolvedEvent of subscription) { - const id = resolvedEvent.event!.id; - - if (nacked[id] != null) { - expect(resolvedEvent.retryCount).toBe(nacked[id]); - } else { - expect(resolvedEvent.retryCount).toBe(0); - } - - await subscription.nack("retry", "to test it", resolvedEvent); - - nacked[id] = (nacked[id] ?? 0) + 1; - - if (resolvedEvent.event?.type === FINISH_TEST) { - break; - } - } - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToStream.test.ts b/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToStream.test.ts deleted file mode 100644 index 9b501a2b..00000000 --- a/packages/test/src/persistentSubscription/subscribeToPersistentSubscriptionToStream.test.ts +++ /dev/null @@ -1,825 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { pipeline, Writable, Readable, Transform } from "stream"; -import { promisify } from "util"; - -import { v4 as uuid } from "uuid"; - -import { - createTestCluster, - Defer, - delay, - jsonTestEvents, - matchServerVersion, - optionalDescribe, - postEventViaHttpApi, -} from "@test-utils"; - -import { - ResolvedEvent, - NotLeaderError, - PersistentSubscriptionToStream, - EventStoreDBClient, - jsonEvent, - persistentSubscriptionToStreamSettingsFromDefaults, - START, -} from "@eventstore/db-client"; - -const asyncPipeline = promisify(pipeline); - -describe("subscribeToPersistentSubscriptionToStream", () => { - const cluster = createTestCluster(); - let client!: EventStoreDBClient; - - const finishEvent = () => - jsonEvent({ - type: "finish-test", - data: { - message: "lets wrap this up", - }, - }); - - beforeAll(async () => { - await cluster.up(); - - client = new EventStoreDBClient( - { endpoints: cluster.endpoints, nodePreference: "leader" }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await cluster.down(); - }); - - describe("should connect to a persistent subscription", () => { - test("from start", async () => { - const STREAM_NAME = "from_start_test_stream_name"; - const GROUP_NAME = "from_start_test_group_name"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents(20)); - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - const defer = new Defer(); - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: ResolvedEvent) => { - if (event.event) { - await subscription.ack(event); - } - - if (event.event?.type === "finish-test") { - defer.resolve(); - } - }); - - const subscription = client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3), - finishEvent(), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - - // 20 pre-write + 4 after - expect(onEvent).toBeCalledTimes(24); - }); - - test("from revision", async () => { - const STREAM_NAME = "from_revision_test_stream_name"; - const GROUP_NAME = "from_revision_test_group_name"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: BigInt(1), - }) - ); - - const defer = new Defer(); - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: ResolvedEvent) => { - await subscription.ack(event); - - if (event.event?.type === "finish-test") { - defer.resolve(); - } - }); - - const subscription = client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3), - finishEvent(), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - - // 4 pre-write + 4 after - 1 (start from revision) - expect(onEvent).toBeCalledTimes(7); - }); - - test("from end", async () => { - const STREAM_NAME = "from_end_test_stream_name"; - const GROUP_NAME = "from_end_test_group_name"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() // end is default - ); - - const defer = new Defer(); - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: ResolvedEvent) => { - await subscription.ack(event); - - if (event.event?.type === "finish-test") { - defer.resolve(); - } - }); - - const subscription = client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3), - finishEvent(), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - - // 4 pre-write + 4 after - 4 (start from end) - expect(onEvent).toBeCalledTimes(4); - }); - - test("nack", async () => { - const STREAM_NAME = "nack_test_stream_name"; - const GROUP_NAME = "nack_test_group_name"; - - // Skip the first twenty events and retry the next 20 events. - // we should see the number of times that the `onEvent` callback - // is called reflects this (if nack is working) - - const skipCount = 20; - const retryCount = 20; - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(skipCount, "skip-event"), - ...jsonTestEvents(retryCount, "retry-event"), - finishEvent(), - ]); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - const defer = new Defer(); - - const nacked: string[] = []; - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(); - const onEvent = jest.fn(async (event: ResolvedEvent) => { - if (!event.event) return; - - if (event.event.type === "finish-test") { - await subscription.ack(event); - defer.resolve(); - return; - } - - if (!nacked.includes(event.event.id)) { - nacked.push(event.event.id); - await subscription.nack( - event.event.type === "skip-event" ? "skip" : "retry", - "To test it", - event - ); - return; - } - - await subscription.ack(event); - return; - }); - - const subscription = client - .subscribeToPersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - await defer.promise; - await subscription.unsubscribe(); - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - - expect(onEvent).toBeCalledTimes( - // skipped - skipCount + - // retried - retryCount * 2 + - // finish test event - 1 - ); - }); - }); - - describe("should return a readable stream", () => { - describe("async iterator", () => { - test("ack", async () => { - const STREAM_NAME = "async_iter_ack"; - const GROUP_NAME = "async_iter_ack_group_name"; - const doSomething = jest.fn(); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - for await (const resolvedEvent of subscription) { - doSomething(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === "finish-test") { - break; - } - } - - expect(doSomething).toBeCalledTimes(100); - }); - - test("nack", async () => { - const STREAM_NAME = "async_iter_nack"; - const GROUP_NAME = "async_iter_nack_group_name"; - const doSomething = jest.fn(); - const nacked: string[] = []; - - // Skip the first twenty events and retry the next 20 events. - // we should see the number of times that the `onEvent` callback - // is called reflects this (if nack is working) - - const skipCount = 20; - const retryCount = 20; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(skipCount, "skip-event"), - ...jsonTestEvents(retryCount, "retry-event"), - finishEvent(), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - for await (const resolvedEvent of subscription) { - if (!resolvedEvent.event) continue; - - doSomething(resolvedEvent); - - if (resolvedEvent.event.type === "finish-test") { - await subscription.ack(resolvedEvent); - break; - } - - if (!nacked.includes(resolvedEvent.event.id)) { - nacked.push(resolvedEvent.event.id); - await subscription.nack( - resolvedEvent.event.type === "skip-event" ? "skip" : "retry", - "To test it", - resolvedEvent - ); - continue; - } - - await subscription.ack(resolvedEvent); - } - - expect(doSomething).toBeCalledTimes( - // skipped - skipCount + - // retried - retryCount * 2 + - // finish test event - 1 - ); - }); - - test("ack with async function", async () => { - const STREAM_NAME = "async_iter_ack_fun"; - const GROUP_NAME = "async_iter_ack_fun_group_name"; - const doSomething = jest.fn(); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - for await (const resolvedEvent of subscription) { - const { event } = resolvedEvent; - if (!event) continue; - - if (event.type === "test") { - // example of awaiting an async function when iterating over the async iterator - await delay(10); - } - - doSomething(event); - - await subscription.ack(resolvedEvent); - - if (event.type === "finish-test") { - break; - } - } - - expect(doSomething).toBeCalledTimes(100); - }); - }); - - test("after the fact event listeners", async () => { - const STREAM_NAME = "after_the_fact"; - const GROUP_NAME = "after_the_fact_group_name"; - - const defer = new Defer(); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - const eventListenerOne = jest.fn(); - const eventListenerTwo = jest.fn(); - const onError = jest.fn(); - const endListener = jest.fn(() => { - defer.resolve(); - }); - - subscription - .on("data", eventListenerOne) - .on("data", async (resolvedEvent) => { - eventListenerTwo(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === "finish-test") { - subscription.unsubscribe(); - } - }) - .on("error", onError) - .on("end", endListener); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(5), - finishEvent(), - ]); - - await defer.promise; - await subscription.unsubscribe(); - - expect(eventListenerOne).toBeCalledTimes(6); - expect(eventListenerTwo).toBeCalledTimes(6); - }); - - test("pipeline", async () => { - const STREAM_NAME = "pipeline_test"; - const GROUP_NAME = "pipeline_test_group_name"; - const FINISH_TEST = "finish_pipeline"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(8), - jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - const acker = new (class extends Transform { - _transform( - resolvedEvent: ResolvedEvent, - _encoding: string, - done: (error: null, e: ResolvedEvent) => void - ) { - subscription.ack(resolvedEvent).then(() => done(null, resolvedEvent)); - } - })({ objectMode: true }); - - const writeStream = new (class extends Writable { - public ids: string[] = []; - _write({ event }: ResolvedEvent, _encoding: string, done: () => void) { - this.ids.push(event!.id); - if (event?.type === FINISH_TEST) { - subscription.unsubscribe().then(done); - } else { - done(); - } - } - })({ objectMode: true }); - - await asyncPipeline(subscription as Readable, acker, writeStream); - - expect(writeStream.ids).toHaveLength(9); - }); - }); - - test("malformed events", async () => { - const STREAM_NAME = "malformed_json"; - const GROUP_NAME = "malformed_json_group_name"; - const doSomething = jest.fn(); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(3, "test 1")); - - const malformedData = "****"; - - await postEventViaHttpApi(cluster, { - contentType: "application/json", - type: "malformed-event", - stream: STREAM_NAME, - data: malformedData, - }); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3, "test 2"), - finishEvent(), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - for await (const resolvedEvent of subscription) { - doSomething(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === "malformed-event") { - expect(resolvedEvent.event.data).toBe(malformedData); - } - - if (resolvedEvent.event?.type === "finish-test") { - break; - } - } - - expect(doSomething).toBeCalledTimes(8); - }); - - test("should throw on follower node", async () => { - // Create connection to a follower node - const followerClient = new EventStoreDBClient( - { - endpoints: cluster.endpoints, - nodePreference: "follower", - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - const STREAM_NAME = "follower_node_test"; - const GROUP_NAME = "follower_node_test"; - const doSomething = jest.fn(); - const confirmThatErrorWasThrown = jest.fn(); - - const createAndConnectWithAutoReconnect = async ( - client: EventStoreDBClient - ): Promise => { - try { - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - return client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - } catch (error) { - confirmThatErrorWasThrown(error); - - // Our command is good, but must be executed on the leader - if (error instanceof NotLeaderError) { - // Create new client connected to the reported leader node - const leaderClient = new EventStoreDBClient( - { - endpoint: error.leader, - }, - { rootCertificate: cluster.certs.root }, - { username: "admin", password: "changeit" } - ); - - // try again with new connection - return createAndConnectWithAutoReconnect(leaderClient); - } - - // other errors can be passed up the chain - throw error; - } - }; - - await followerClient.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(), - ]); - - const subscription = await createAndConnectWithAutoReconnect( - followerClient - ); - - for await (const resolvedEvent of subscription) { - doSomething(resolvedEvent); - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event?.type === "finish-test") { - break; - } - } - - expect(doSomething).toBeCalledTimes(100); - - expect(confirmThatErrorWasThrown).toBeCalledTimes(1); - expect(confirmThatErrorWasThrown.mock.calls[0][0]).toBeInstanceOf( - NotLeaderError - ); - }); - - test("on system stream", async () => { - await client.enableProjection("$by_event_type"); - - const FINISH_ID = uuid(); - const EVENT_TYPE = "SourceCreatedEvent"; - const SYSTEM_STREAM_NAME = `$et-${EVENT_TYPE}`; - const GROUP_NAME = "system_stream"; - - const STREAM_NAME = "system_stream"; - const doSomething = jest.fn(); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99, EVENT_TYPE), - jsonEvent({ - type: EVENT_TYPE, - id: FINISH_ID, - data: { - message: "test", - index: 100, - }, - }), - ]); - - await client.createPersistentSubscriptionToStream( - SYSTEM_STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - resolveLinkTos: true, - }) - ); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - SYSTEM_STREAM_NAME, - GROUP_NAME - ); - - const acked = new Set(); - - for await (const resolvedEvent of subscription) { - if (!resolvedEvent.event) continue; - - doSomething(resolvedEvent); - - expect(acked).not.toContain(resolvedEvent.event?.id); - - acked.add(resolvedEvent.event.id); - - await subscription.ack(resolvedEvent); - - if (resolvedEvent.event.id === FINISH_ID) { - await subscription.unsubscribe(); - } - } - - expect(doSomething).toBeCalledTimes(100); - }); - - test("retryCount", async () => { - const STREAM_NAME = "retry_count"; - const GROUP_NAME = "retry_count_group_name"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - maxRetryCount: 5, - }) - ); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(99), - finishEvent(), - ]); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - const nacked: Record = {}; - - for await (const resolvedEvent of subscription) { - const id = resolvedEvent.event!.id; - - if (nacked[id] != null) { - expect(resolvedEvent.retryCount).toBe(nacked[id]); - } else { - expect(resolvedEvent.retryCount).toBe(0); - } - - await subscription.nack("retry", "to test it", resolvedEvent); - - nacked[id] = (nacked[id] ?? 0) + 1; - - if (resolvedEvent.event?.type === "finish-test") { - break; - } - } - }); - - const supported = matchServerVersion`>=22.6.0`; - optionalDescribe(supported)("Supported (>=22.6.0)", () => { - test("populates log position", async () => { - const STREAM_NAME = "log_position"; - const GROUP_NAME = "log_position_group_name"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }) - ); - - const appendResult = await client.appendToStream( - STREAM_NAME, - finishEvent() - ); - - const subscription = client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - for await (const resolvedEvent of subscription) { - await subscription.ack(resolvedEvent); - - expect(resolvedEvent.event?.position).toBeDefined(); - expect(resolvedEvent.event?.position?.commit).toBeDefined(); - expect(resolvedEvent.event?.position?.prepare).toBeDefined(); - expect(resolvedEvent.event?.position?.commit).toBe( - appendResult.position?.commit - ); - expect(resolvedEvent.event?.position?.prepare).toBe( - appendResult.position?.prepare - ); - - if (resolvedEvent.event?.type === "finish-test") { - break; - } - } - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/updatePersistentSubscriptionToAll.test.ts b/packages/test/src/persistentSubscription/updatePersistentSubscriptionToAll.test.ts deleted file mode 100644 index f031485b..00000000 --- a/packages/test/src/persistentSubscription/updatePersistentSubscriptionToAll.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; - -import { - PINNED, - EventStoreDBClient, - persistentSubscriptionToAllSettingsFromDefaults, - UnsupportedError, -} from "@eventstore/db-client"; - -describe("updatePersistentSubscriptionToAll", () => { - const supported = matchServerVersion`>=21.10`; - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10)", () => { - test("Throws an unavailable error", async () => { - const GROUP_NAME = "oh_no"; - - try { - await client.updatePersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - } catch (error) { - expect(error).toBeInstanceOf(UnsupportedError); - expect(error).toMatchInlineSnapshot( - `[Error: updatePersistentSubscriptionToAll requires server version 21.10 or higher.]` - ); - } - }); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - test("should update a persistent subscription to all", async () => { - const GROUP_NAME = "supported_test_group_name"; - const settings = persistentSubscriptionToAllSettingsFromDefaults({ - extraStatistics: true, - }); - - await client.createPersistentSubscriptionToAll(GROUP_NAME, settings); - - await expect( - client.updatePersistentSubscriptionToAll(GROUP_NAME, { - ...settings, - consumerStrategyName: PINNED, - }) - ).resolves.toBeUndefined(); - }); - }); -}); diff --git a/packages/test/src/persistentSubscription/updatePersistentSubscriptionToStream.test.ts b/packages/test/src/persistentSubscription/updatePersistentSubscriptionToStream.test.ts deleted file mode 100644 index f1cdd5e8..00000000 --- a/packages/test/src/persistentSubscription/updatePersistentSubscriptionToStream.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { createTestNode } from "@test-utils"; - -import { - PINNED, - EventStoreDBClient, - persistentSubscriptionToStreamSettingsFromDefaults, -} from "@eventstore/db-client"; - -describe("updatePersistentSubscriptionToStream", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { - endpoint: node.uri, - }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("should update a persistent subscription", async () => { - const STREAM_NAME = "from_start_test_stream_name"; - const GROUP_NAME = "from_start_test_group_name"; - const settings = persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: BigInt(1), - }); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - settings - ); - - await expect( - client.updatePersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME, { - ...settings, - consumerStrategyName: PINNED, - }) - ).resolves.toBeUndefined(); - }); -}); diff --git a/packages/test/src/plugins/__snapshots__/client-certificates.deprecated.test.ts.snap b/packages/test/src/plugins/__snapshots__/client-certificates.deprecated.test.ts.snap deleted file mode 100644 index 5c4b3471..00000000 --- a/packages/test/src/plugins/__snapshots__/client-certificates.deprecated.test.ts.snap +++ /dev/null @@ -1,29 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`client certificates (with deprecated credential options) show warnings and/or error when constructor is initialised with incorrect channel credentials combinations constructor initialised with both privateKey and certChain 1`] = ` -Array [ - Array [ - "The certChain and privateKey options have been deprecated and will be removed in the next major version. Please use userCertFile and userKeyFile instead.", - ], -] -`; - -exports[`client certificates (with deprecated credential options) show warnings and/or error when constructor is initialised with incorrect channel credentials combinations constructor initialised with certChain only 1`] = `[Error: Certificate chain must be given with accompanying private key]`; - -exports[`client certificates (with deprecated credential options) show warnings and/or error when constructor is initialised with incorrect channel credentials combinations constructor initialised with certChain only 2`] = ` -Array [ - Array [ - "The certChain and privateKey options have been deprecated and will be removed in the next major version. Please use userCertFile and userKeyFile instead.", - ], -] -`; - -exports[`client certificates (with deprecated credential options) show warnings and/or error when constructor is initialised with incorrect channel credentials combinations constructor initialised with privateKey only 1`] = `[Error: Private key must be given with accompanying certificate chain]`; - -exports[`client certificates (with deprecated credential options) show warnings and/or error when constructor is initialised with incorrect channel credentials combinations constructor initialised with privateKey only 2`] = ` -Array [ - Array [ - "The certChain and privateKey options have been deprecated and will be removed in the next major version. Please use userCertFile and userKeyFile instead.", - ], -] -`; diff --git a/packages/test/src/plugins/__snapshots__/client-certificates.test.ts.snap b/packages/test/src/plugins/__snapshots__/client-certificates.test.ts.snap deleted file mode 100644 index afce78ea..00000000 --- a/packages/test/src/plugins/__snapshots__/client-certificates.test.ts.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`client certificates throw error when constructor is initialised with incorrect channel credentials combinations connection string with userCertFile only 1`] = `[Error: userCertFile must be given with accompanying userKeyFile]`; - -exports[`client certificates throw error when constructor is initialised with incorrect channel credentials combinations connection string with userKeyFile only 1`] = `[Error: userCertFile must be given with accompanying userKeyFile]`; - -exports[`client certificates throw error when constructor is initialised with incorrect channel credentials combinations constructor initialised with userCertFile only 1`] = `[Error: Certificate chain must be given with accompanying private key]`; - -exports[`client certificates throw error when constructor is initialised with incorrect channel credentials combinations constructor initialised with userKeyFile only 1`] = `[Error: Private key must be given with accompanying certificate chain]`; diff --git a/packages/test/src/plugins/client-certificates.deprecated.test.ts b/packages/test/src/plugins/client-certificates.deprecated.test.ts deleted file mode 100644 index 2be05e59..00000000 --- a/packages/test/src/plugins/client-certificates.deprecated.test.ts +++ /dev/null @@ -1,160 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -/** - * Test constructor with deprecated fields, certChain and privateKey in the client. - * NOTE: This test should be removed in the next major relase. - */ - -import { createTestNode, jsonTestEvents } from "@test-utils"; -import { - AccessDeniedError, - ChannelCredentialOptions, - EventStoreDBClient, -} from "@eventstore/db-client"; - -describe("client certificates (with deprecated credential options)", () => { - const node = createTestNode(); - - beforeAll(async () => { - await node.up(); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("show warnings and/or error when constructor is initialised with incorrect channel credentials combinations", () => { - test.each([ - [ - "certChain only", - () => - new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - certChain: node.certs.users.admin.userCertFile, - } - ), - ], - [ - "privateKey only", - () => - new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - privateKey: node.certs.users.admin.userKeyFile, - } - ), - ], - [ - "both privateKey and certChain", - () => - new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - privateKey: node.certs.users.admin.userKeyFile, - certChain: node.certs.users.admin.userCertFile, - } - ), - ], - ])("constructor initialised with %s", (_, makeCall) => { - const warnSpy = jest.spyOn(console, "warn").mockImplementation(); - try { - makeCall(); - } catch (error) { - expect(error).toMatchSnapshot(); - } - - expect(warnSpy).toHaveBeenCalled(); - expect(warnSpy.mock.calls).toMatchSnapshot(); - - warnSpy.mockRestore(); - }); - }); - - describe("client initialized with only the admin certificate", () => { - let client: EventStoreDBClient; - - beforeEach(() => { - client = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - certChain: node.certs.users.admin.userCertFile, - privateKey: node.certs.users.admin.userKeyFile, - } - ); - }); - - test("using default only certificates", async () => { - const result = await client.appendToStream( - "using_default_only_certificates", - jsonTestEvents(2) - ); - expect(result).toBeDefined(); - }); - - test("overridden user credentials takes precedence over invalid user certificate", async () => { - await expect( - client.appendToStream( - "overridden_user_credentials_takes_precedence", - jsonTestEvents(2), - { - credentials: { - username: "wrong", - password: "password", - }, - } - ) - ).rejects.toThrow(AccessDeniedError); - }); - }); - - test("user credentials takes precedence over the client certificate during initialization", async () => { - const clientWithCredentials = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - certChain: node.certs.users.admin.userCertFile, - privateKey: node.certs.users.admin.userKeyFile, - }, - { - username: "wrong", - password: "password", - } - ); - - await expect( - clientWithCredentials.appendToStream( - "user_credentials_takes_precedence_over_client_certificate", - jsonTestEvents(2) - ) - ).rejects.toThrow(AccessDeniedError); - }); - - test("When the client is initialized with invalid certificate, user credentials take precendence if overriden during a call", async () => { - const clientWithBadCertificate = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - certChain: node.certs.users.invalid.userCertFile, - privateKey: node.certs.users.invalid.userKeyFile, - } - ); - - expect( - await clientWithBadCertificate.appendToStream( - "user_credentials_takes_precedence_over_client_certificate", - jsonTestEvents(2), - { - credentials: { - username: "admin", - password: "changeit", - }, - } - ) - ).toBeDefined(); - }); -}); diff --git a/packages/test/src/plugins/client-certificates.test.ts b/packages/test/src/plugins/client-certificates.test.ts deleted file mode 100644 index b5e026e9..00000000 --- a/packages/test/src/plugins/client-certificates.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { createTestNode, jsonTestEvents } from "@test-utils"; -import { AccessDeniedError, EventStoreDBClient } from "@eventstore/db-client"; - -describe("client certificates", () => { - const node = createTestNode(); - - beforeAll(async () => { - await node.up(); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("throw error when constructor is initialised with incorrect channel credentials combinations", () => { - test.each([ - [ - "userCertFile", - () => - new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - userCertFile: node.certs.users.admin.userCertFile, - } - ), - ], - [ - "userKeyFile", - () => - new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - userKeyFile: node.certs.users.admin.userKeyFile, - } - ), - ], - ])("constructor initialised with %s only", (_, makeCall) => { - try { - makeCall(); - } catch (error) { - expect(error).toMatchSnapshot(); - } - }); - - test.each([ - [ - "userCertFile", - () => - EventStoreDBClient.connectionString`esdb://${node.uri}?tls=true&tlsCAFile=${node.certPath.root}&userCertFile=${node.certPath.admin.certPath}`, - ], - [ - "userKeyFile", - () => - EventStoreDBClient.connectionString`esdb://${node.uri}?tls=true&tlsCAFile=${node.certPath.root}&userKeyFile=${node.certPath.admin.certKeyPath}`, - ], - ])("connection string with %s only", (_, connection) => { - try { - connection(); - } catch (error) { - expect(error).toMatchSnapshot(); - } - }); - }); - - describe("client initialized with only the admin certificate", () => { - let client: EventStoreDBClient; - - beforeEach(() => { - client = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - userCertFile: node.certs.users.admin.userCertFile, - userKeyFile: node.certs.users.admin.userKeyFile, - } - ); - }); - - test("using default only certificates", async () => { - const result = await client.appendToStream( - "using_default_only_certificates", - jsonTestEvents(2) - ); - expect(result).toBeDefined(); - }); - - test("overridden user credentials takes precedence over invalid user certificate", async () => { - await expect( - client.appendToStream( - "overridden_user_credentials_takes_precedence", - jsonTestEvents(2), - { - credentials: { - username: "wrong", - password: "password", - }, - } - ) - ).rejects.toThrow(AccessDeniedError); - }); - }); - - test("user credentials takes precedence over the client certificate during initialization", async () => { - const clientWithCredentials = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - userCertFile: node.certs.users.admin.userCertFile, - userKeyFile: node.certs.users.admin.userKeyFile, - }, - { - username: "wrong", - password: "password", - } - ); - - await expect( - clientWithCredentials.appendToStream( - "user_credentials_takes_precedence_over_client_certificate", - jsonTestEvents(2) - ) - ).rejects.toThrow(AccessDeniedError); - }); - - test("When the client is initialized with invalid certificate, user credentials take precendence if overriden during a call", async () => { - const clientWithBadCertificate = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - userCertFile: node.certs.users.invalid.userCertFile, - userKeyFile: node.certs.users.invalid.userKeyFile, - } - ); - - expect( - await clientWithBadCertificate.appendToStream( - "user_credentials_takes_precedence_over_client_certificate", - jsonTestEvents(2), - { - credentials: { - username: "admin", - password: "changeit", - }, - } - ) - ).toBeDefined(); - }); -}); diff --git a/packages/test/src/projections/createProjection.test.ts b/packages/test/src/projections/createProjection.test.ts deleted file mode 100644 index a2b52609..00000000 --- a/packages/test/src/projections/createProjection.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { collect, createTestNode, delay } from "@test-utils"; - -import { - EventStoreDBClient, - jsonEvent, - StreamNotFoundError, -} from "@eventstore/db-client"; - -describe("createProjection", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("defaults", async () => { - const PROJECTION_NAME = "defaults"; - - await expect( - client.createProjection( - PROJECTION_NAME, - ` - fromAll() - .when({ - $init: function (state, ev) { - return {}; - } - }); - ` - ) - ).resolves.toBeUndefined(); - }); - - test("emitEnabled", async () => { - const PROJECTION_NAME = "emit"; - const STREAM_NAME = "emit"; - const EVENT_TYPE = "emit_this"; - const EMIT_STREAMNAME = "emit_emitted"; - const EMIT_EVENT_TYPE = "emitted_this"; - const projection = ` - fromStream("${STREAM_NAME}") - .when({ - ${EVENT_TYPE}(state, event) { - emit("${EMIT_STREAMNAME}", "${EMIT_EVENT_TYPE}", {}, {}); - } - }); - `; - - await client.createProjection(PROJECTION_NAME, projection, { - emitEnabled: true, - }); - - await client.appendToStream( - STREAM_NAME, - jsonEvent({ type: EVENT_TYPE, data: "anything" }) - ); - - await delay(500); - - const [emitted] = await collect(client.readStream(EMIT_STREAMNAME)); - - expect(emitted).toBeDefined(); - expect(emitted.event?.type).toBe(EMIT_EVENT_TYPE); - - try { - for await (const e of client.readStream( - `$projections-${PROJECTION_NAME}-emittedstreams` - )) { - expect(e).toBe("UNREACHABLE"); - } - } catch (error) { - expect(error).toBeInstanceOf(StreamNotFoundError); - } - }); - - test("trackEmittedStreams", async () => { - const PROJECTION_NAME = "track_and_field"; - const STREAM_NAME = "track_and_field"; - const EVENT_TYPE = "emit_this"; - const EMIT_STREAMNAME = "track_and_field_emitted"; - const EMIT_EVENT_TYPE = "emitted_this"; - const projection = ` - fromStream("${STREAM_NAME}") - .when({ - ${EVENT_TYPE}(state, event) { - emit("${EMIT_STREAMNAME}", "${EMIT_EVENT_TYPE}", {}, {}); - } - }); - `; - - await client.createProjection(PROJECTION_NAME, projection, { - emitEnabled: true, - trackEmittedStreams: true, - }); - - await client.appendToStream( - STREAM_NAME, - jsonEvent({ type: EVENT_TYPE, data: "anything" }) - ); - - await delay(500); - - const [emitted] = await collect(client.readStream(EMIT_STREAMNAME)); - - expect(emitted).toBeDefined(); - expect(emitted.event?.type).toBe(EMIT_EVENT_TYPE); - - const [emittedStream] = await collect( - client.readStream(`$projections-${PROJECTION_NAME}-emittedstreams`) - ); - - expect(emittedStream).toBeDefined(); - expect(emittedStream.event?.type).toBe("$StreamTracked"); - }); -}); diff --git a/packages/test/src/projections/deleteProjection.test.ts b/packages/test/src/projections/deleteProjection.test.ts deleted file mode 100644 index ce8b5877..00000000 --- a/packages/test/src/projections/deleteProjection.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { createTestNode, matchServerVersion } from "@test-utils"; - -import { - EventStoreDBClient, - RUNNING, - DELETING, - STOPPED, - ABORTED, - NotFoundError, - UnknownError, -} from "@eventstore/db-client"; - -describe("deleteProjection", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const projection = ` - fromAll() - .when({ - $init: function (state, ev) { - return { - last: ev, - }; - }, - }); - `; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("delete the projection", async () => { - const PROJECTION_NAME = "projection_to_delete_everything"; - - await client.createProjection(PROJECTION_NAME, projection); - - const beforeDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(beforeDetails).toBeDefined(); - expect(beforeDetails.projectionStatus).toBe(RUNNING); - - await client.disableProjection(PROJECTION_NAME); - - const disabledDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(disabledDetails).toBeDefined(); - - // Incorrect projection status was switched (ABORTED -> STOPPED) in - // https://github.com/EventStore/EventStore/pull/2944 - expect([STOPPED, ABORTED]).toContain(disabledDetails.projectionStatus); - - if (disabledDetails.projectionStatus === ABORTED) { - // before https://github.com/EventStore/EventStore/pull/2944 - // writeCheckpoint had to be false to stop the projection - await client.abortProjection(PROJECTION_NAME); - - const stoppedDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(stoppedDetails).toBeDefined(); - expect(stoppedDetails.projectionStatus).toBe(STOPPED); - } - - await client.deleteProjection(PROJECTION_NAME, { - deleteCheckpointStream: true, - deleteStateStream: true, - }); - - try { - const afterDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(afterDetails).toBeDefined(); - expect(afterDetails.projectionStatus).toBe(DELETING); - } catch (error) { - // projection is already deleted - expect(error).toBeInstanceOf( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - } - }); - - describe("errors", () => { - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - - await expect( - client.deleteProjection(PROJECTION_NAME) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); - }); -}); diff --git a/packages/test/src/projections/disableProjection.test.ts b/packages/test/src/projections/disableProjection.test.ts deleted file mode 100644 index 86995564..00000000 --- a/packages/test/src/projections/disableProjection.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { createTestNode, matchServerVersion } from "@test-utils"; - -import { - ABORTED, - EventStoreDBClient, - NotFoundError, - RUNNING, - STOPPED, - UnknownError, -} from "@eventstore/db-client"; - -describe("disable / abort", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const projection = ` - fromAll() - .when({ - $init: function (state, ev) { - return { - last: ev, - }; - }, - }); - `; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("disableProjection", () => { - test("disables the projection", async () => { - const PROJECTION_NAME = "projection_to_disable"; - - await client.createProjection(PROJECTION_NAME, projection); - - const beforeDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(beforeDetails).toBeDefined(); - expect(beforeDetails.projectionStatus).toBe(RUNNING); - - await client.disableProjection(PROJECTION_NAME); - - const afterDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(afterDetails).toBeDefined(); - - if (matchServerVersion`>=21.10`) { - expect(afterDetails.projectionStatus).toBe(STOPPED); - } else { - // Incorrect projection status was switched (ABORTED -> STOPPED) in - // https://github.com/EventStore/EventStore/pull/2944 - expect([STOPPED, ABORTED]).toContain(afterDetails.projectionStatus); - } - }); - }); - - describe("errors", () => { - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - - await expect( - client.disableProjection(PROJECTION_NAME) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); - }); - - describe("abortProjection", () => { - test("aborts the projection", async () => { - const PROJECTION_NAME = "projection_to_abort"; - - await client.createProjection(PROJECTION_NAME, projection); - - const beforeDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(beforeDetails).toBeDefined(); - expect(beforeDetails.projectionStatus).toBe(RUNNING); - - await client.abortProjection(PROJECTION_NAME); - - const afterDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(afterDetails).toBeDefined(); - - if (matchServerVersion`>=21.10`) { - expect(afterDetails.projectionStatus).toBe(ABORTED); - } else { - // Incorrect projection status was switched (ABORTED -> STOPPED) in - // https://github.com/EventStore/EventStore/pull/2944 - expect([STOPPED, ABORTED]).toContain(afterDetails.projectionStatus); - } - }); - - describe("errors", () => { - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - - await expect( - client.abortProjection(PROJECTION_NAME) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); - }); - }); -}); diff --git a/packages/test/src/projections/enableProjection.test.ts b/packages/test/src/projections/enableProjection.test.ts deleted file mode 100644 index a3bf179c..00000000 --- a/packages/test/src/projections/enableProjection.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { createTestNode, matchServerVersion } from "@test-utils"; - -import { - ABORTED, - EventStoreDBClient, - NotFoundError, - RUNNING, - STOPPED, - UnknownError, -} from "@eventstore/db-client"; - -describe("enableProjection", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const projection = ` - fromAll() - .when({ - $init: function (state, ev) { - return { - last: ev, - }; - }, - }); - `; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("enables the projection", async () => { - const PROJECTION_NAME = "projection_to_enable"; - - await client.createProjection(PROJECTION_NAME, projection); - - await client.disableProjection(PROJECTION_NAME); - - const beforeDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(beforeDetails).toBeDefined(); - - if (matchServerVersion`>=21.10`) { - expect(beforeDetails.projectionStatus).toBe(STOPPED); - } else { - // Incorrect projection status was switched (ABORTED -> STOPPED) in - // https://github.com/EventStore/EventStore/pull/2944 - expect([STOPPED, ABORTED]).toContain(beforeDetails.projectionStatus); - } - - await client.enableProjection(PROJECTION_NAME); - - const afterDetails = await client.getProjectionStatus(PROJECTION_NAME); - - expect(afterDetails).toBeDefined(); - expect(afterDetails.projectionStatus).toBe(RUNNING); - }); - - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - - await expect(client.enableProjection(PROJECTION_NAME)).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); -}); diff --git a/packages/test/src/projections/getProjectionResult.test.ts b/packages/test/src/projections/getProjectionResult.test.ts deleted file mode 100644 index deb51da8..00000000 --- a/packages/test/src/projections/getProjectionResult.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - delay, - jsonTestEvents, - matchServerVersion, -} from "@test-utils"; - -import { - RUNNING, - EventStoreDBClient, - jsonEvent, - NotFoundError, - UnknownError, -} from "@eventstore/db-client"; - -describe("getProjectionResult", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.enableProjection("$by_category"); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("gets the result of a projection", () => { - test("without partition", async () => { - const PROJECTION_NAME = "count events"; - const EVENT_TYPE = "count_this"; - const STREAM_NAME = "some_stream_name"; - const projection = ` - fromStream("${STREAM_NAME}") - .when({ - $init() { - return 0; - }, - ${EVENT_TYPE}(state, event) { - return state + 1 - } - }); - `; - - const count = 12; - - await client.createProjection(PROJECTION_NAME, projection); - - await client.appendToStream( - STREAM_NAME, - jsonTestEvents(count, EVENT_TYPE) - ); - - await delay(5000); - - const state = await client.getProjectionResult(PROJECTION_NAME); - - expect(state).toBe(count); - }); - - test("with partition", async () => { - const PARTITION_PROJECTION_NAME = "partition_projection"; - const COUNTER_PROJECTION_NAME = "cat_activity_counter"; - - const STREAM_NAME = "What cat is doing"; - - const NAPPED_EVENT = "napped"; - const SNACKED_EVENT = "snacked"; - - type CatActivity = typeof NAPPED_EVENT | typeof SNACKED_EVENT; - type CatCounter = Record; - - const SMUDGES = "Smudges"; - const PEANUT_BUTTER = "PeanutButter"; - const MR_WHISKERS = "MrWhiskers"; - - const paritionProjection = ` - fromStream("${STREAM_NAME}") - .when({ - $any: function(state, ev) { - linkTo('cat-' + ev.data.catName, ev) - } - }); - `; - - const countProjection = ` - fromCategory('cat') - .foreachStream() - .when({ - "$init": function(state, ev) { - return { naps: 0, snacks: 0 }; - }, - "${NAPPED_EVENT}": function(state, ev) { - state.naps++; - }, - "${SNACKED_EVENT}": function(state, ev) { - state.snacks++; - } - }); - `; - - const createCatEvent = (catName: string, type: string) => { - return jsonEvent({ - type, - data: { - catName, - }, - }); - }; - - await client.createProjection( - PARTITION_PROJECTION_NAME, - paritionProjection, - { emitEnabled: true } - ); - - const partitionStats = await client.getProjectionStatus( - PARTITION_PROJECTION_NAME - ); - - expect(partitionStats.projectionStatus).toBe(RUNNING); - - await client.createProjection(COUNTER_PROJECTION_NAME, countProjection); - - const counterStats = await client.getProjectionStatus( - COUNTER_PROJECTION_NAME - ); - - expect(counterStats.projectionStatus).toBe(RUNNING); - - const byCategoryStats = await client.getProjectionStatus("$by_category"); - - expect(byCategoryStats.projectionStatus).toBe(RUNNING); - - await client.appendToStream(STREAM_NAME, [ - createCatEvent(MR_WHISKERS, NAPPED_EVENT), - createCatEvent(SMUDGES, NAPPED_EVENT), - createCatEvent(MR_WHISKERS, SNACKED_EVENT), - createCatEvent(PEANUT_BUTTER, NAPPED_EVENT), - createCatEvent(MR_WHISKERS, NAPPED_EVENT), - createCatEvent(PEANUT_BUTTER, SNACKED_EVENT), - ]); - - await delay(5000); - - const result = await client.getProjectionResult( - COUNTER_PROJECTION_NAME, - { partition: `cat-${MR_WHISKERS}` } - ); - - expect(result).toMatchObject({ - naps: 2, - snacks: 1, - }); - }); - }); - - describe("errors", () => { - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - - await expect( - client.getProjectionResult(PROJECTION_NAME) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); - }); -}); diff --git a/packages/test/src/projections/getProjectionState.test.ts b/packages/test/src/projections/getProjectionState.test.ts deleted file mode 100644 index 6c0bf0a9..00000000 --- a/packages/test/src/projections/getProjectionState.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - delay, - jsonTestEvents, - matchServerVersion, -} from "@test-utils"; - -import { - EventStoreDBClient, - jsonEvent, - NotFoundError, - RUNNING, - UnknownError, -} from "@eventstore/db-client"; - -describe("getProjectionState", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - await client.enableProjection("$by_category"); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("gets the state of a projection", () => { - test("without partition", async () => { - const PROJECTION_NAME = "count events"; - const EVENT_TYPE = "count_this"; - const STREAM_NAME = "some_stream_name"; - const projection = ` - fromStream("${STREAM_NAME}") - .when({ - $init() { - return 0; - }, - ${EVENT_TYPE}(state, event) { - return state + 1 - } - }); - `; - const count = 3; - - await client.createProjection(PROJECTION_NAME, projection); - - await client.appendToStream( - STREAM_NAME, - jsonTestEvents(count, EVENT_TYPE) - ); - - await delay(5000); - - const state = await client.getProjectionState(PROJECTION_NAME); - - expect(state).toBe(count); - }); - - test("with partition", async () => { - const PARTITION_PROJECTION_NAME = "partition_projection"; - const COUNTER_PROJECTION_NAME = "cat_activity_counter"; - - const STREAM_NAME = "What cat is doing"; - - const NAPPED_EVENT = "napped"; - const SNACKED_EVENT = "snacked"; - - type CatActivity = typeof NAPPED_EVENT | typeof SNACKED_EVENT; - type CatCounter = Record; - - const SMUDGES = "Smudges"; - const PEANUT_BUTTER = "PeanutButter"; - const MR_WHISKERS = "MrWhiskers"; - - const paritionProjection = ` - fromStream("${STREAM_NAME}") - .when({ - $any: function(state, ev) { - linkTo('cat-' + ev.data.catName, ev) - } - }); - `; - - const countProjection = ` - fromCategory('cat') - .foreachStream() - .when({ - "$init": function(state, ev) { - return { naps: 0, snacks: 0 }; - }, - "${NAPPED_EVENT}": function(state, ev) { - state.naps++; - }, - "${SNACKED_EVENT}": function(state, ev) { - state.snacks++; - } - }); - `; - - const createCatEvent = (catName: string, type: string) => { - return jsonEvent({ - type, - data: { - catName, - }, - }); - }; - - await client.createProjection( - PARTITION_PROJECTION_NAME, - paritionProjection, - { emitEnabled: true } - ); - - const partitionStats = await client.getProjectionStatus( - PARTITION_PROJECTION_NAME - ); - - expect(partitionStats.projectionStatus).toBe(RUNNING); - - await client.createProjection(COUNTER_PROJECTION_NAME, countProjection); - - const counterStats = await client.getProjectionStatus( - COUNTER_PROJECTION_NAME - ); - - expect(counterStats.projectionStatus).toBe(RUNNING); - - const byCategoryStats = await client.getProjectionStatus("$by_category"); - - expect(byCategoryStats.projectionStatus).toBe(RUNNING); - - await client.appendToStream(STREAM_NAME, [ - createCatEvent(MR_WHISKERS, NAPPED_EVENT), - createCatEvent(SMUDGES, NAPPED_EVENT), - createCatEvent(MR_WHISKERS, SNACKED_EVENT), - createCatEvent(PEANUT_BUTTER, NAPPED_EVENT), - createCatEvent(MR_WHISKERS, NAPPED_EVENT), - createCatEvent(PEANUT_BUTTER, SNACKED_EVENT), - createCatEvent(MR_WHISKERS, SNACKED_EVENT), - createCatEvent(MR_WHISKERS, NAPPED_EVENT), - ]); - - await delay(5000); - - const state = await client.getProjectionState( - COUNTER_PROJECTION_NAME, - { partition: `cat-${MR_WHISKERS}` } - ); - - expect(state).toMatchObject({ - naps: 3, - snacks: 2, - }); - }); - }); - - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - - await expect( - client.getProjectionState(PROJECTION_NAME) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); -}); diff --git a/packages/test/src/projections/getProjectionStatus.test.ts b/packages/test/src/projections/getProjectionStatus.test.ts deleted file mode 100644 index d6762dd2..00000000 --- a/packages/test/src/projections/getProjectionStatus.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { createTestNode, matchServerVersion } from "@test-utils"; - -import { - EventStoreDBClient, - NotFoundError, - UnknownError, -} from "@eventstore/db-client"; - -describe("getProjectionStatus", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const basicProjection = ` - fromAll() - .when({ - $init: function (state, ev) { - return {}; - } - }); - `; - - const projections = ["projection-1", "projection-2", "projection-3"]; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - for (const name of projections) { - await client.createProjection(name, basicProjection); - } - }); - - afterAll(async () => { - await node.down(); - }); - - describe("gets Projection Status", () => { - test("gets status", async () => { - const REQUESTED_NAME = projections[2]; - - const details = await client.getProjectionStatus(REQUESTED_NAME); - - expect(details).toBeDefined(); - expect(details.name).toBe(REQUESTED_NAME); - }); - - test("non-existant", async () => { - const REQUESTED_NAME = "some-non-existant-projection"; - await expect( - client.getProjectionStatus(REQUESTED_NAME) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); - }); -}); diff --git a/packages/test/src/projections/listProjections.test.ts b/packages/test/src/projections/listProjections.test.ts deleted file mode 100644 index ca5112ad..00000000 --- a/packages/test/src/projections/listProjections.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createTestNode } from "@test-utils"; - -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("list projections", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const basicProjection = ` - fromAll() - .when({ - $init: function (state, ev) { - return {}; - } - }); - `; - - const projectionNames = ["projection-1", "projection-2", "projection-3"]; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - for (const name of projectionNames) { - await client.createProjection(name, basicProjection); - } - }); - - afterAll(async () => { - await node.down(); - }); - - test("lists projections", async () => { - const projections = await client.listProjections(); - - // includes system projections - expect(projections.length).toBeGreaterThan(projectionNames.length); - - projections.forEach((details) => { - expect(details).toBeDefined(); - - if (!details.name.startsWith("$")) { - expect(projectionNames).toContain(details.name); - } - }); - }); -}); diff --git a/packages/test/src/projections/resetProjection.test.ts b/packages/test/src/projections/resetProjection.test.ts deleted file mode 100644 index c6b94046..00000000 --- a/packages/test/src/projections/resetProjection.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { createTestNode, matchServerVersion } from "@test-utils"; - -import { - EventStoreDBClient, - NotFoundError, - UnknownError, -} from "@eventstore/db-client"; - -describe("resetProjection", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const projection = ` - fromAll() - .when({ - $init: function (state, ev) { - return { - last: ev, - }; - }, - }); - `; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("resets the projection", async () => { - const PROJECTION_NAME = "projection_to_disable"; - await client.createProjection(PROJECTION_NAME, projection); - await client.resetProjection(PROJECTION_NAME); - }); - - describe("errors", () => { - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - await expect( - client.resetProjection(PROJECTION_NAME) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); - }); -}); diff --git a/packages/test/src/projections/restartSubsystem.test.ts b/packages/test/src/projections/restartSubsystem.test.ts deleted file mode 100644 index 9a407cbb..00000000 --- a/packages/test/src/projections/restartSubsystem.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createTestNode } from "@test-utils"; - -import { EventStoreDBClient } from "@eventstore/db-client"; - -describe("restartSubsystem", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("Doesnt error", async () => { - await expect(client.restartSubsystem()).resolves.toBeUndefined(); - }); -}); diff --git a/packages/test/src/projections/updateProjection.test.ts b/packages/test/src/projections/updateProjection.test.ts deleted file mode 100644 index bcb928c3..00000000 --- a/packages/test/src/projections/updateProjection.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { createTestNode, matchServerVersion } from "@test-utils"; - -import { - EventStoreDBClient, - NotFoundError, - UnknownError, -} from "@eventstore/db-client"; - -describe("resetProjection", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const projection = ` - fromAll() - .when({ - $init: function (state, ev) { - return { - last: ev, - }; - }, - }); - `; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("resets the projection", () => { - test("change query", async () => { - const PROJECTION_NAME = "projection_to_update_query"; - const after = ` - fromAll() - .when({ - $init: function (state, ev) { - return { - last: ev, - updated: true, - }; - }, - }); - `; - await client.createProjection(PROJECTION_NAME, projection); - await client.updateProjection(PROJECTION_NAME, after); - }); - - test("Emit enabled", async () => { - const PROJECTION_NAME = "projection_to_update_tracking"; - await client.createProjection(PROJECTION_NAME, projection); - await client.updateProjection(PROJECTION_NAME, projection, { - emitEnabled: true, - }); - }); - }); - - describe("errors", () => { - test("projection doesnt exist", async () => { - const PROJECTION_NAME = "doesnt exist"; - await expect( - client.updateProjection(PROJECTION_NAME, projection) - ).rejects.toThrowError( - matchServerVersion`>=24.6` ? NotFoundError : UnknownError - ); - }); - }); -}); diff --git a/packages/test/src/samples/appending-events.ts b/packages/test/src/samples/appending-events.ts deleted file mode 100644 index c4c04572..00000000 --- a/packages/test/src/samples/appending-events.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { - jsonEvent, - NO_STREAM, - START, - FORWARDS, - EventStoreDBClient, - JSONEventType, - AppendExpectedRevision, - WrongExpectedVersionError, -} from "@eventstore/db-client"; -import { createTestNode } from "@test-utils"; -import { v4 as uuid } from "uuid"; - -describe("[sample] appending-events", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - test("append-to-stream", async () => { - // region append-to-stream - type SomeEvent = JSONEventType< - "some-event", - { - id: string; - value: string; - } - >; - - const event = jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "1", - value: "some value", - }, - }); - - await client.appendToStream("some-stream", event, { - expectedRevision: NO_STREAM, - }); - // endregion append-to-stream - }); - - test("append-duplicate-event", async () => { - type SomeEvent = JSONEventType< - "some-event", - { - id: string; - value: string; - } - >; - - // region append-duplicate-event - const event = jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "1", - value: "some value", - }, - }); - - await client.appendToStream("same-event-stream", event); - - // attempt to append the same event again - await client.appendToStream("same-event-stream", event); - // endregion append-duplicate-event - }); - - test("append-with-no-stream", async () => { - type SomeEvent = JSONEventType< - "some-event", - { - id: string; - value: string; - } - >; - try { - // region append-with-no-stream - const eventOne = jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "1", - value: "some value", - }, - }); - - const eventTwo = jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "2", - value: "some other value", - }, - }); - - await client.appendToStream("no-stream-stream", eventOne, { - expectedRevision: NO_STREAM, - }); - - // attempt to append the same event again - await client.appendToStream("no-stream-stream", eventTwo, { - expectedRevision: NO_STREAM, - }); - // endregion append-with-no-stream - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - } - }); - - test("append-with-concurrency-check", async () => { - type SomeEvent = JSONEventType< - "some-event", - { - id: string; - value: string; - } - >; - try { - await client.appendToStream( - "concurrency-stream", - jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "1", - value: "some value", - }, - }) - ); - - // region append-with-concurrency-check - const events = client.readStream("concurrency-stream", { - fromRevision: START, - direction: FORWARDS, - }); - - let revision: AppendExpectedRevision = NO_STREAM; - for await (const { event } of events) { - revision = event?.revision ?? revision; - } - - const clientOneEvent = jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "1", - value: "some value", - }, - }); - - await client.appendToStream("concurrency-stream", clientOneEvent, { - expectedRevision: revision, - }); - - const clientTwoEvent = jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "2", - value: "some value", - }, - }); - - await client.appendToStream("concurrency-stream", clientTwoEvent, { - expectedRevision: revision, - }); - // endregion append-with-concurrency-check - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - } - }); - - test("overriding-user-credentials", async () => { - const event = jsonEvent({ - id: uuid(), - type: "some-event", - data: { - id: "1", - value: "some value", - }, - }); - // region overriding-user-credentials - const credentials = { - username: "admin", - password: "changeit", - }; - - await client.appendToStream("some-stream", event, { - credentials, - }); - // endregion overriding-user-credentials - }); -}); diff --git a/packages/test/src/samples/get-started.ts b/packages/test/src/samples/get-started.ts deleted file mode 100644 index 1938b6f8..00000000 --- a/packages/test/src/samples/get-started.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - EventStoreDBClient, - jsonEvent, - START, - FORWARDS, - JSONEventType, -} from "@eventstore/db-client"; -import { optionalDescribe } from "@test-utils"; -import { v4 as uuid } from "uuid"; - -const CLOUD_ID = process.env.EVENTSTORE_CLOUD_ID!; -const STREAM_NAME = uuid(); - -/* -// region createClient -const client = EventStoreDBClient.connectionString`{connectionString}`; -// endregion createClient -*/ - -optionalDescribe(!!CLOUD_ID)("[sample] get-started", () => { - test("get-started", async () => { - const client = EventStoreDBClient.connectionString`esdb+discover://${CLOUD_ID}.mesdb.eventstore.cloud`; - - // region createEvent - type TestEvent = JSONEventType< - "TestEvent", - { - entityId: string; - importantData: string; - } - >; - - const event = jsonEvent({ - type: "TestEvent", - data: { - entityId: uuid(), - importantData: "I wrote my first event!", - }, - }); - // endregion createEvent - - // region appendEvents - await client.appendToStream(STREAM_NAME, event); - // endregion appendEvents - - // region readStream - const events = client.readStream(STREAM_NAME, { - direction: FORWARDS, - fromRevision: START, - maxCount: 10, - }); - // endregion readStream - - for await (const resolvedEvent of events) { - expect(resolvedEvent.event?.data.importantData).toBe( - event.data.importantData - ); - } - }); -}); diff --git a/packages/test/src/samples/opentelemetry.ts b/packages/test/src/samples/opentelemetry.ts deleted file mode 100644 index d6047590..00000000 --- a/packages/test/src/samples/opentelemetry.ts +++ /dev/null @@ -1,104 +0,0 @@ -// region import-required-packages -import { - InMemorySpanExporter, - NodeTracerProvider, - ConsoleSpanExporter, - SimpleSpanProcessor, -} from "@opentelemetry/sdk-trace-node"; -import { registerInstrumentations } from "@opentelemetry/instrumentation"; -import { EventStoreDBInstrumentation } from "@eventstore/opentelemetry"; -import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc"; -import {} from "@opentelemetry/sdk-trace-node"; -// endregion import-required-packages -import { createTestNode } from "@test-utils"; -import { EventStoreDBClient } from "@eventstore/db-client"; - -import * as esdb from "@eventstore/db-client"; - -// region register-instrumentation -const provider = new NodeTracerProvider(); - -const instrumentation = new EventStoreDBInstrumentation(); - -registerInstrumentations({ - instrumentations: [instrumentation], - tracerProvider: provider, -}); -// endregion register-instrumentation - -instrumentation.disable(); - -describe("[sample] opentelemetry", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - // region setup-exporter - const memoryExporter = new InMemorySpanExporter(); - const otlpExporter = new OTLPTraceExporter({ url: "http://localhost:4317" }); // change this to your OTLP receiver address - const consoleExporter = new ConsoleSpanExporter(); - - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter)); - provider.addSpanProcessor(new SimpleSpanProcessor(otlpExporter)); - // endregion setup-exporter - - // @ts-expect-error the moduleExports property is private. This is needed to make the test work with auto-mocking - instrumentation._modules[0].moduleExports = esdb; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - beforeAll(async () => { - await node.up(); - instrumentation.enable(); - }); - - afterAll(async () => { - instrumentation.disable(); - await node.down(); - }); - - afterEach(() => { - memoryExporter.reset(); - }); - - test("tracing", async () => { - // region setup-client-for-tracing - const { EventStoreDBClient, jsonEvent } = await import( - "@eventstore/db-client" - ); - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - // endregion setup-client-for-tracing - - const response = await client.appendToStream( - "some-stream", - jsonEvent({ - type: "OrderPlaced", - data: { - orderId: "1337", - orderValue: 123.45, - }, - }), - { - expectedRevision: "any", - } - ); - - expect(response).toBeDefined(); - - const memorySpans = memoryExporter.getFinishedSpans(); - - expect(memorySpans.length).toBe(1); - }); -}); diff --git a/packages/test/src/samples/persistent-subscriptions.ts b/packages/test/src/samples/persistent-subscriptions.ts deleted file mode 100644 index 5d7a551b..00000000 --- a/packages/test/src/samples/persistent-subscriptions.ts +++ /dev/null @@ -1,401 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - AllStreamResolvedEvent, - EventStoreDBClient, - jsonEvent, - JSONEventType, - PARK, - persistentSubscriptionToStreamSettingsFromDefaults, - persistentSubscriptionToAllSettingsFromDefaults, - ResolvedEvent, - START, - streamNameFilter, -} from "@eventstore/db-client"; -import { - createTestNode, - jsonTestEvents, - matchServerVersion, - optionalTest, -} from "@test-utils"; - -type SomeEvent = JSONEventType<"test", Record>; - -describe("[sample] persistent-subscriptions", () => { - const supports$all = matchServerVersion`>=21.10`; - const node = createTestNode(); - const log = console.log; - - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream("some-stream", jsonTestEvents()); - console.log = jest.fn(log); - }); - - afterAll(async () => { - console.log = log; - await node.down(); - }); - - afterEach(() => { - (console.log as jest.Mock).mockReset(); - }); - - test("create-persistent-subscription-to-stream", async () => { - const STREAM_NAME = "create-persistent-subscription-to-stream"; - const GROUP_NAME = "create-persistent-subscription-to-stream-group"; - - // region create-persistent-subscription-to-stream - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults(), - { credentials: { username: "admin", password: "changeit" } } - ); - // endregion create-persistent-subscription-to-stream - }); - - test("subscribe-to-persistent-subscription-to-stream", async () => { - const STREAM_NAME = "subscribe-to-persistent-subscription-to-stream"; - const GROUP_NAME = "subscribe-to-persistent-subscription-to-stream-group"; - const handleEvent = async (event: ResolvedEvent) => { - expect(event).toBeDefined(); - await subscription.unsubscribe(); - }; - - await client.appendToStream( - STREAM_NAME, - jsonEvent({ type: "test", data: {} }) - ); - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ startFrom: START }), - { credentials: { username: "admin", password: "changeit" } } - ); - - // region subscribe-to-persistent-subscription-to-stream - const subscription = - client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - try { - for await (const event of subscription) { - try { - console.log( - `handling event ${event.event?.type} with retryCount ${event.retryCount}` - ); - await handleEvent(event); - await subscription.ack(event); - } catch (error) { - await subscription.nack(PARK, error.toString(), event); - } - } - } catch (error) { - console.log(`Subscription was dropped. ${error}`); - } - // endregion subscribe-to-persistent-subscription-to-stream - }); - - optionalTest(supports$all)( - "create-persistent-subscription-to-all", - async () => { - const GROUP_NAME = "create-persistent-subscription-to-all-group"; - - // region create-persistent-subscription-to-all - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults(), - { - filter: streamNameFilter({ prefixes: ["test"] }), - credentials: { username: "admin", password: "changeit" }, - } - ); - // endregion create-persistent-subscription-to-all - } - ); - - optionalTest(supports$all)( - "subscribe-to-persistent-subscription-to-all", - async () => { - const STREAM_NAME = "subscribe-to-persistent-subscription-to-all"; - const GROUP_NAME = "subscribe-to-persistent-subscription-to-all-group"; - const handleEvent = async (event: AllStreamResolvedEvent) => { - expect(event).toBeDefined(); - await subscription.unsubscribe(); - }; - - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults(), - { - filter: streamNameFilter({ prefixes: [STREAM_NAME] }), - } - ); - - await client.appendToStream( - STREAM_NAME, - jsonEvent({ type: "test", data: {} }) - ); - - // region subscribe-to-persistent-subscription-to-all - const subscription = - client.subscribeToPersistentSubscriptionToAll(GROUP_NAME); - - try { - for await (const event of subscription) { - console.log( - `handling event ${event.event?.type} with retryCount ${event.retryCount}` - ); - await handleEvent(event); - await subscription.ack(event); - } - } catch (error) { - console.log(`Subscription was dropped. ${error}`); - } - - // endregion subscribe-to-persistent-subscription-to-all - } - ); - - test("subscribe-to-persistent-subscription-with-manual-acks", async () => { - const STREAM_NAME = "subscribe-to-persistent-subscription-with-manual-acks"; - const GROUP_NAME = - "subscribe-to-persistent-subscription-with-manual-acks-group"; - const handleEvent = async (event: ResolvedEvent) => { - expect(event).toBeDefined(); - await subscription.unsubscribe(); - }; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - startFrom: START, - }), - { credentials: { username: "admin", password: "changeit" } } - ); - - await client.appendToStream( - STREAM_NAME, - jsonEvent({ type: "test", data: {} }) - ); - - // region subscribe-to-persistent-subscription-with-manual-acks - const subscription = - client.subscribeToPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME - ); - - try { - for await (const event of subscription) { - try { - console.log( - `handling event ${event.event?.type} with retryCount ${event.retryCount}` - ); - await handleEvent(event); - await subscription.ack(event); - } catch (error) { - await subscription.nack(PARK, error.toString(), event); - } - } - } catch (error) { - console.log(`Subscription was dropped. ${error}`); - } - // endregion subscribe-to-persistent-subscription-with-manual-acks - }); - - test("update-persistent-subscription", async () => { - const STREAM_NAME = "update-persistent-subscription"; - const GROUP_NAME = "update-persistent-subscription-group"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ); - - // region update-persistent-subscription - await client.updatePersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults({ - resolveLinkTos: true, - checkPointLowerBound: 20, - }) - ); - // endregion update-persistent-subscription - }); - - test("delete-persistent-subscription", async () => { - const STREAM_NAME = "delete-persistent-subscription"; - const GROUP_NAME = "delete-persistent-subscription-group"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ); - - // region delete-persistent-subscription - await client.deletePersistentSubscriptionToStream(STREAM_NAME, GROUP_NAME); - // endregion delete-persistent-subscription - }); - - test("get-persistent-subscription-to-stream-info", async () => { - const STREAM_NAME = "get-persistent-subscription-to-stream-info"; - const GROUP_NAME = "get-persistent-subscription-to-stream-info-group"; - - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ); - - // region get-persistent-subscription-to-stream-info - const info = await client.getPersistentSubscriptionToStreamInfo( - STREAM_NAME, - GROUP_NAME - ); - - console.log( - `Persistent subscription ${info.groupName} to stream ${info.eventSource} has status ${info.status}.` - ); - // endregion get-persistent-subscription-to-stream-info - - expect(console.log).toBeCalledWith( - `Persistent subscription ${GROUP_NAME} to stream ${STREAM_NAME} has status ${info.status}.` - ); - }); - - optionalTest(supports$all)( - "get-persistent-subscription-to-all-info", - async () => { - const GROUP_NAME = "get-persistent-subscription-to-all-info-group"; - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - - // region get-persistent-subscription-to-all-info - const info = await client.getPersistentSubscriptionToAllInfo(GROUP_NAME); - - console.log( - `Persistent subscription ${info.groupName} to $all has status ${info.status}.` - ); - // endregion get-persistent-subscription-to-all-info - - expect(console.log).toBeCalledWith( - `Persistent subscription ${GROUP_NAME} to $all has status ${info.status}.` - ); - } - ); - - test("replay-parked-of-persistent-subscription-to-stream", async () => { - const STREAM_NAME = "replay-parked-of-persistent-subscription-to-stream"; - const GROUP_NAME = - "replay-parked-of-persistent-subscription-to-stream-group"; - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ); - - // region replay-parked-of-persistent-subscription-to-stream - await client.replayParkedMessagesToStream(STREAM_NAME, GROUP_NAME, { - stopAt: 10, - }); - // endregion replay-parked-of-persistent-subscription-to-stream - }); - - optionalTest(supports$all)( - "replay-parked-of-persistent-subscription-to-all", - async () => { - const GROUP_NAME = - "replay-parked-of-persistent-subscription-to-all-group"; - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - // region replay-parked-of-persistent-subscription-to-all - await client.replayParkedMessagesToAll(GROUP_NAME, { stopAt: 10 }); - // endregion replay-parked-of-persistent-subscription-to-all - } - ); - - test("list-persistent-subscriptions-to-stream", async () => { - const STREAM_NAME = "list-persistent-subscriptions-to-stream"; - const GROUP_NAME = "list-persistent-subscriptions-to-stream-group"; - await client.createPersistentSubscriptionToStream( - STREAM_NAME, - GROUP_NAME, - persistentSubscriptionToStreamSettingsFromDefaults() - ); - // region list-persistent-subscriptions-to-stream - const subscriptions = await client.listPersistentSubscriptionsToStream( - STREAM_NAME - ); - - for (const { groupName, eventSource, status } of subscriptions) { - console.log( - `Persistent subscription ${groupName} to stream ${eventSource} has status ${status}.` - ); - } - // endregion list-persistent-subscriptions-to-stream - - expect(console.log).toBeCalledTimes(1); - }); - - optionalTest(supports$all)( - "list-persistent-subscriptions-to-all", - async () => { - const GROUP_NAME = "list-persistent-subscriptions-to-all-group"; - await client.createPersistentSubscriptionToAll( - GROUP_NAME, - persistentSubscriptionToAllSettingsFromDefaults() - ); - // region list-persistent-subscriptions-to-all - const subscriptions = await client.listPersistentSubscriptionsToAll(); - - for (const { groupName, status } of subscriptions) { - console.log( - `Persistent subscription ${groupName} to $all has status ${status}.` - ); - } - // endregion list-persistent-subscriptions-to-all - - expect(console.log).toBeCalled(); - } - ); - - test("list-persistent-subscriptions", async () => { - // region list-persistent-subscriptions - const subscriptions = await client.listAllPersistentSubscriptions(); - - for (const { groupName, eventSource, status } of subscriptions) { - console.log( - `Persistent subscription ${groupName} to ${eventSource} has status ${status}.` - ); - } - // endregion list-persistent-subscriptions - }); - - test("restart-persistent-subscription-subsystem", async () => { - // region restart-persistent-subscription-subsystem - await client.restartPersistentSubscriptionSubsystem(); - // endregion restart-persistent-subscription-subsystem - }); -}); diff --git a/packages/test/src/samples/projection-management.ts b/packages/test/src/samples/projection-management.ts deleted file mode 100644 index 9548f044..00000000 --- a/packages/test/src/samples/projection-management.ts +++ /dev/null @@ -1,370 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { v4 as uuid } from "uuid"; - -import { EventStoreDBClient, isCommandError } from "@eventstore/db-client"; -import { - createTestNode, - delay, - jsonTestEvents, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; - -optionalDescribe(matchServerVersion`<=23.10`)( - "[sample] projection-management", - () => { - const noop = (...args: unknown[]) => { - // do nothing - }; - const node = createTestNode(); - const log = console.log; - - let client!: EventStoreDBClient; - - const createTestProjection = async ( - name: string = uuid(), - retry = 5 - ): Promise => { - try { - await client.createProjection(name, "fromAll().when()"); - } catch (error) { - if (retry > 0) return createTestProjection(name, retry - 1); - throw error; - } - - return name; - }; - - beforeAll(async () => { - await node.up(); - - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream("some-stream", jsonTestEvents()); - console.log = jest.fn(log); - }); - - afterAll(async () => { - console.log = log; - await node.down(); - }); - - afterEach(() => { - (console.log as jest.Mock).mockReset(); - }); - - test("createClient", async () => { - const ENDPOINT = node.uri; - const ADMIN = "admin"; - const PASSWORD = "changeit"; - - // region createClient - const client = EventStoreDBClient.connectionString` - esdb+discover://${ADMIN}:${PASSWORD}@${ENDPOINT}?nodePreference=leader - `; - // endregion createClient - - noop(client); - }); - - test("CreateContinuous", async () => { - // region CreateContinuous - const name = `countEvents_Create_${uuid()}`; - const projection = ` - fromAll() - .when({ - $init() { - return { - count: 0, - }; - }, - $any(s, e) { - s.count += 1; - } - }) - .outputState(); - `; - await client.createProjection(name, projection); - // endregion CreateContinuous - - // region CreateContinuous_Conflict - try { - await client.createProjection(name, projection); - } catch (err) { - if (!isCommandError(err) || !err.message.includes("Conflict")) - throw err; - console.log(`${name} already exists`); - } - // endregion CreateContinuous_Conflict - - expect(console.log).toHaveBeenCalledTimes(1); - }); - - test("Enable", async () => { - // region Enable - await client.enableProjection("$by_category"); - // endregion Enable - }); - - test("EnableNotFound", async () => { - // region EnableNotFound - const projectionName = "projection that does not exist"; - - try { - await client.enableProjection(projectionName); - } catch (err) { - if (!isCommandError(err) || !err.message.includes("NotFound")) - throw err; - console.log(`${projectionName} does not exist`); - } - // endregion EnableNotFound - expect(console.log).toHaveBeenCalledTimes(1); - }); - - test("Disable", async () => { - // region Disable - await client.disableProjection("$by_category"); - // endregion Disable - }); - - test("DisableNotFound", async () => { - // region DisableNotFound - const projectionName = "projection that does not exist"; - - try { - await client.disableProjection(projectionName); - } catch (err) { - if (!isCommandError(err) || !err.message.includes("NotFound")) - throw err; - console.log(`${projectionName} does not exist`); - } - // endregion DisableNotFound - expect(console.log).toHaveBeenCalledTimes(1); - }); - - test("Delete", async () => { - const name = await createTestProjection(); - - // before https://github.com/EventStore/EventStore/pull/2944 - // writeCheckpoint had to be false (abort) to stop the projection - await client.abortProjection(name); - - // region Delete - // A projection must be disabled to allow it to be deleted. - await client.disableProjection(name); - - // The projection can now be deleted - await client.deleteProjection(name); - // endregion Delete - }); - - test("DeleteNotFound", async () => { - // region DeleteNotFound - const projectionName = "projection that does not exist"; - - try { - await client.deleteProjection(projectionName); - } catch (err) { - if (!isCommandError(err) || !err.message.includes("NotFound")) - throw err; - console.log(`${projectionName} does not exist`); - } - // endregion DeleteNotFound - expect(console.log).toHaveBeenCalledTimes(1); - }); - - test("Abort", async () => { - const name = await createTestProjection(); - - // region Abort - await client.abortProjection(name); - // endregion Abort - }); - - test("Abort_NotFound", async () => { - // region Abort_NotFound - const projectionName = "projection that does not exist"; - - try { - await client.abortProjection(projectionName); - } catch (err) { - if (!isCommandError(err) || !err.message.includes("NotFound")) - throw err; - console.log(`${projectionName} does not exist`); - } - // endregion Abort_NotFound - expect(console.log).toHaveBeenCalledTimes(1); - }); - - test("Reset", async () => { - const name = await createTestProjection(); - - // region Reset - await client.resetProjection(name); - // endregion Reset - }); - - test("Reset_NotFound", async () => { - // region Reset_NotFound - const projectionName = "projection that does not exist"; - - try { - await client.resetProjection(projectionName); - } catch (err) { - if (!isCommandError(err) || !err.message.includes("NotFound")) - throw err; - console.log(`${projectionName} does not exist`); - } - // endregion Reset_NotFound - expect(console.log).toHaveBeenCalledTimes(1); - }); - - test("Update", async () => { - // region Update - const name = `countEvents_Update_${uuid()}`; - const projection = ` - fromAll() - .when({ - $init() { - return { - count: 0, - }; - }, - $any(s, e) { - s.count += 1; - } - }) - .outputState(); - `; - await client.createProjection(name, "fromAll().when()"); - await client.updateProjection(name, projection); - // endregion Update - }); - - test("Update_NotFound", async () => { - // region Update_NotFound - const projectionName = "projection that does not exist"; - - try { - await client.updateProjection(projectionName, "fromAll().when()"); - } catch (err) { - if (!isCommandError(err) || !err.message.includes("NotFound")) - throw err; - console.log(`${projectionName} does not exist`); - } - // endregion Update_NotFound - expect(console.log).toHaveBeenCalledTimes(1); - }); - - test("ListAll", async () => { - // region ListAll - // This is currently not available in the nodejs client - // endregion ListAll - }); - - test("ListContinuous", async () => { - // region ListContinuous - const projections = await client.listProjections(); - - for (const { name, status, checkpointStatus, progress } of projections) { - console.log(name, status, checkpointStatus, progress); - } - // endregion ListContinuous - }); - - test("GetStatus", async () => { - const name = await createTestProjection(); - - // region GetStatus - const projection = await client.getProjectionStatus(name); - - console.log( - projection.name, - projection.status, - projection.checkpointStatus, - projection.progress - ); - // endregion GetStatus - }); - - test("GetState", async () => { - // region GetState - interface CountProjectionState { - count: number; - } - - const name = `get_state_example`; - const projection = ` - fromAll() - .when({ - $init() { - return { - count: 0, - }; - }, - $any(s, e) { - s.count += 1; - } - }) - .transformBy((state) => state.count) - .outputState(); - `; - - await client.createProjection(name, projection); - - // Give it some time to count event - await delay(500); - - const state = await client.getProjectionState(name); - - console.log(`Counted ${state.count} events.`); - // endregion GetState - - expect(console.log).toBeCalledTimes(1); - expect(typeof state.count).toBe("number"); - }); - - test("GetResult", async () => { - // region GetResult - const name = `get_result_example`; - const projection = ` - fromAll() - .when({ - $init() { - return { - count: 0, - }; - }, - $any(s, e) { - s.count += 1; - } - }) - .transformBy((state) => state.count) - .outputState(); - `; - - await client.createProjection(name, projection); - - // Give it some time to have a result. - await delay(500); - - const result = await client.getProjectionResult(name); - - console.log(`Counted ${result} events.`); - // endregion GetResult - expect(console.log).toHaveBeenCalledTimes(1); - expect(typeof result).toBe("number"); - }); - - test("RestartSubSystem", async () => { - // region RestartSubSystem - await client.restartSubsystem(); - // endregion RestartSubSystem - }); - } -); diff --git a/packages/test/src/samples/reading-events.ts b/packages/test/src/samples/reading-events.ts deleted file mode 100644 index 2f6d541c..00000000 --- a/packages/test/src/samples/reading-events.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { - START, - FORWARDS, - EventStoreDBClient, - StreamNotFoundError, - BACKWARDS, - END, - JSONEventType, -} from "@eventstore/db-client"; -import { createTestNode, jsonTestEvents } from "@test-utils"; - -type SomeEvent = JSONEventType< - "some-event", - { - id: string; - value: string; - } ->; - -describe("[sample] reading-events", () => { - const log = console.log; - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream("some-stream", jsonTestEvents()); - console.log = jest.fn(); - }); - - afterAll(async () => { - console.log = log; - await node.down(); - }); - - test("read-from-stream", async () => { - // region read-from-stream - const events = client.readStream("some-stream", { - direction: FORWARDS, - fromRevision: START, - maxCount: 10, - }); - // endregion read-from-stream - - // #region iterate-stream - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - // #endregion iterate-stream - }); - - test("read-from-stream-position", async () => { - // region read-from-stream-position - const events = client.readStream("some-stream", { - direction: FORWARDS, - fromRevision: BigInt(10), - maxCount: 20, - }); - // endregion read-from-stream-position - - // #region iterate-stream - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - // #endregion iterate-stream - }); - - test("read-from-stream-position-check", async () => { - // region checking-for-stream-presence - - const events = client.readStream("some-stream", { - direction: FORWARDS, - fromRevision: BigInt(10), - maxCount: 20, - }); - - try { - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - } catch (error) { - if (error instanceof StreamNotFoundError) { - return; - } - - throw error; - } - - // #endregion checking-for-stream-presence - }); - - test("read-from-stream-overriding-credentials", async () => { - // region overriding-user-credentials - const credentials = { - username: "admin", - password: "changeit", - }; - - const events = client.readStream("some-stream", { - direction: FORWARDS, - fromRevision: START, - credentials, - maxCount: 10, - }); - // endregion overriding-user-credentials - - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - }); - - test("read-from-stream-backwards", async () => { - // region reading-backwards - const events = client.readStream("some-stream", { - direction: BACKWARDS, - fromRevision: END, - maxCount: 10, - }); - - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - // #endregion reading-backwards - }); - - test("read-from-all-stream", async () => { - // region read-from-all-stream - const events = client.readAll({ - direction: FORWARDS, - fromPosition: START, - maxCount: 10, - }); - // endregion read-from-all-stream - - // #region read-from-all-stream-iterate - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - // #endregion read-from-all-stream-iterate - }); - - test("ignore-system-events", async () => { - // region ignore-system-events - const events = client.readAll({ - direction: FORWARDS, - fromPosition: START, - maxCount: 10, - }); - - for await (const resolvedEvent of events) { - if (resolvedEvent.event?.type.startsWith("$")) { - continue; - } - - console.log(resolvedEvent.event?.type); - } - // #endregion ignore-system-events - }); - - test("read-from-all-stream-backwards", async () => { - // region read-from-all-stream-backwards - const events = client.readAll({ - direction: BACKWARDS, - fromPosition: END, - maxCount: 10, - }); - // endregion read-from-all-stream-backwards - - // #region read-from-all-stream-iterate - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - // #endregion read-from-all-stream-iterate - - return events; - }); - - test("read-from-all-overriding-credentials", async () => { - // region read-all-overriding-user-credentials - const credentials = { - username: "admin", - password: "changeit", - }; - - const events = client.readAll({ - direction: FORWARDS, - fromPosition: START, - credentials, - maxCount: 10, - }); - // endregion read-all-overriding-user-credentials - - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - }); - - test("filter-out-system-events", async () => { - // region filter-out-system-events - const events = client.readAll({ - direction: FORWARDS, - fromPosition: START, - maxCount: 10, - }); - - for await (const resolvedEvent of events) { - if (resolvedEvent.event?.type.startsWith("$")) { - continue; - } - console.log(resolvedEvent.event?.type); - } - // #endregion filter-out-system-events - }); - - test("read-from-all-stream-resolving-link-tos", async () => { - // region read-from-all-stream-resolving-link-Tos - const events = client.readAll({ - direction: BACKWARDS, - fromPosition: END, - resolveLinkTos: true, - maxCount: 10, - }); - // endregion read-from-all-stream-resolving-link-Tos - - // #region read-from-all-stream-iterate - for await (const resolvedEvent of events) { - console.log(resolvedEvent.event?.data); - } - // #endregion read-from-all-stream-iterate - }); -}); diff --git a/packages/test/src/samples/server-side-filtering.ts b/packages/test/src/samples/server-side-filtering.ts deleted file mode 100644 index 3bc94f58..00000000 --- a/packages/test/src/samples/server-side-filtering.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { - START, - EventStoreDBClient, - excludeSystemEvents, - eventTypeFilter, - streamNameFilter, -} from "@eventstore/db-client"; -import { createTestNode, jsonTestEvents } from "@test-utils"; - -describe("[sample] server-side-filtering", () => { - const log = console.log; - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream("some-stream", jsonTestEvents()); - console.log = jest.fn(); - }); - - afterAll(async () => { - console.log = log; - await node.down(); - }); - - test("exclude-system", async () => { - // region exclude-system - const subscription = client - .subscribeToAll({ - fromPosition: START, - filter: excludeSystemEvents(), - }) - .on("data", (resolvedEvent) => { - console.log( - `Received event ${resolvedEvent.event?.revision}@${resolvedEvent.event?.streamId}` - ); - }); - // endregion exclude-system - await subscription.unsubscribe(); - }); - - test("event-type-prefix", async () => { - // region event-type-prefix - const filter = eventTypeFilter({ - prefixes: ["customer-"], - }); - // endregion event-type-prefix - - return filter; - }); - - test("event-type-regex", async () => { - // region event-type-regex - const filter = eventTypeFilter({ - regex: "^user|^company", - }); - // endregion event-type-regex - - return filter; - }); - - test("stream-prefix", async () => { - // region stream-prefix - const filter = streamNameFilter({ - prefixes: ["user-"], - }); - // endregion stream-prefix - return filter; - }); - - test("stream-regex", async () => { - // region stream-regex - const filter = streamNameFilter({ - regex: "^account|^savings", - }); - // endregion stream-regex - return filter; - }); - - test("checkpoint", async () => { - const doSomethingAsync = async () => { - // :shrug: - }; - // region checkpoint - excludeSystemEvents({ - async checkpointReached(_subscription, position) { - // The subscription will wait until the promise is resolved - await doSomethingAsync(); - console.log(`checkpoint taken at ${position.prepare}`); - }, - }); - // endregion checkpoint - }); - - test("checkpoint-with-interval", async () => { - // region checkpoint-with-interval - const filter = eventTypeFilter({ - regex: "^[^$].*", - checkpointInterval: 1000, - checkpointReached(_subscription, position) { - console.log(`checkpoint taken at ${position.prepare}`); - }, - }); - // endregion checkpoint-with-interval - return filter; - }); -}); diff --git a/packages/test/src/samples/subscribing-to-streams.ts b/packages/test/src/samples/subscribing-to-streams.ts deleted file mode 100644 index 0556c900..00000000 --- a/packages/test/src/samples/subscribing-to-streams.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { - START, - EventStoreDBClient, - END, - ReadRevision, - JSONEventType, - ResolvedEvent, - AllStreamResolvedEvent, - streamNameFilter, - ReadPosition, -} from "@eventstore/db-client"; -import { createTestNode, jsonTestEvents } from "@test-utils"; - -describe("[sample] server-side-filtering", () => { - const log = console.log; - const node = createTestNode(); - const handleEvent = jest.fn(); - let client!: EventStoreDBClient; - - type SomeStreamEvents = - | JSONEventType<"a", { a: true }> - | JSONEventType<"b", { b: true }>; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream("some-stream", jsonTestEvents()); - console.log = jest.fn(); - }); - - afterAll(async () => { - console.log = log; - await node.down(); - }); - - test("subscribe-to-stream", async () => { - // region subscribe-to-stream - const subscription = - client.subscribeToStream("some-stream"); - - for await (const resolvedEvent of subscription) { - console.log( - `Received event ${resolvedEvent.event?.revision}@${resolvedEvent.event?.streamId}` - ); - await handleEvent(resolvedEvent); - } - // endregion subscribe-to-stream - - async function handleEvent(_event: ResolvedEvent) { - await subscription.unsubscribe(); - } - }); - - test("subscribe-to-stream-from-position", async () => { - // region subscribe-to-stream-from-position - const subscription = client.subscribeToStream( - "some-stream", - { - fromRevision: BigInt(20), - } - ); - // endregion subscribe-to-stream-from-position - - await subscription.unsubscribe(); - }); - - test("subscribe-to-stream-live", async () => { - // region subscribe-to-stream-live - const subscription = client.subscribeToStream( - "some-stream", - { - fromRevision: END, - } - ); - // endregion subscribe-to-stream-live - - await subscription.unsubscribe(); - }); - - test("subscribe-to-stream-resolving-linktos", async () => { - // region subscribe-to-stream-resolving-linktos - const subscription = client.subscribeToStream( - "$et-myEventType", - { - fromRevision: START, - resolveLinkTos: true, - } - ); - // endregion subscribe-to-stream-resolving-linktos - - await subscription.unsubscribe(); - }); - - test("subscribe-to-stream-subscription-dropped", async () => { - // region subscribe-to-stream-subscription-dropped - let checkpoint: ReadRevision = START; - - const subscription = client - .subscribeToStream("some-stream", { - fromRevision: checkpoint, - }) - .on("data", (resolvedEvent) => { - handleEvent(resolvedEvent); - checkpoint = resolvedEvent.event?.revision ?? checkpoint; - }); - // endregion subscribe-to-stream-subscription-dropped - - subscription.unsubscribe(); - }); - - test("subscribeToAll", async () => { - // region subscribe-to-all - const subscription = client.subscribeToAll(); - - for await (const resolvedEvent of subscription) { - console.log( - `Received event ${resolvedEvent.event?.revision}@${resolvedEvent.event?.streamId}` - ); - await handleEvent(resolvedEvent); - } - // endregion subscribe-to-all - - async function handleEvent(_event: AllStreamResolvedEvent) { - await subscription.unsubscribe(); - } - }); - - test("subscribe-to-all-from-position", async () => { - // region subscribe-to-all-from-position - const subscription = client.subscribeToAll({ - fromPosition: { - commit: BigInt(1056), - prepare: BigInt(1056), - }, - }); - // endregion subscribe-to-all-from-position - - await subscription.unsubscribe(); - }); - - test("subscribe-to-all-live", async () => { - // region subscribe-to-all-live - const subscription = client.subscribeToAll({ - fromPosition: END, - }); - // endregion subscribe-to-all-live - - await subscription.unsubscribe(); - }); - - test("stream-prefix-filtered-subscription", async () => { - // region stream-prefix-filtered-subscription - const subscription = client.subscribeToAll({ - filter: streamNameFilter({ prefixes: ["test-", "other-"] }), - }); - // endregion stream-prefix-filtered-subscription - - await subscription.unsubscribe(); - }); - - test("subscribe-to-all-subscription-dropped", async () => { - // region subscribe-to-all-subscription-dropped - let checkpoint: ReadPosition = START; - - const subscription = client - .subscribeToAll({ - fromPosition: checkpoint, - }) - .on("data", (resolvedEvent) => { - handleEvent(resolvedEvent); - checkpoint = resolvedEvent.event?.position ?? checkpoint; - }); - // endregion subscribe-to-all-subscription-dropped - - await subscription.unsubscribe(); - }); - - test("subscribeToAllOverridingUserCredentials", async () => { - // region overriding-user-credentials - const subscription = client.subscribeToStream( - "some-stream", - { - credentials: { - username: "admin", - password: "changeit", - }, - } - ); - // endregion overriding-user-credentials - - await subscription.unsubscribe(); - }); -}); diff --git a/packages/test/src/samples/user-certificates.ts b/packages/test/src/samples/user-certificates.ts deleted file mode 100644 index 9bfa7258..00000000 --- a/packages/test/src/samples/user-certificates.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { EventStoreDBClient } from "@eventstore/db-client"; -import { createTestNode, jsonTestEvents } from "@test-utils"; -import { v4 as uuid } from "uuid"; - -const STREAM_NAME = uuid(); - -describe("[sample] user certificates", () => { - const node = createTestNode(); - - beforeAll(async () => { - await node.up(); - }); - - afterAll(async () => { - await node.down(); - }); - - test("connection string", async () => { - const connectionStringTemplate = `esdb://admin:changeit@{endpoint}?tls=true&userCertFile={pathToCaFile}&userKeyFile={pathToKeyFile}`; - - try { - // region client-with-user-certificates - const connectionString = `esdb://admin:changeit@{endpoint}?tls=true&userCertFile={pathToCaFile}&userKeyFile={pathToKeyFile}`; - const client = EventStoreDBClient.connectionString(connectionString); - // endregion client-with-user-certificates - - expect(connectionString).toBe(connectionStringTemplate); - } catch (error) { - // do nothing. - } - - const endpoints = node.endpoints - .map((endpoint) => `${endpoint.address}:${endpoint.port}`) - .join(","); - let connectionStringTest = connectionStringTemplate - .replace("{endpoint}", endpoints) - .replace("{pathToCaFile}", node.certPath.admin.certPath) - .replace("{pathToKeyFile}", node.certPath.admin.certKeyPath); - - connectionStringTest = `${connectionStringTest}&tlsCaFile=${node.certPath.root}`; - - const client = EventStoreDBClient.connectionString(connectionStringTest); - - const result = await client.appendToStream(STREAM_NAME, jsonTestEvents(2)); - expect(result).toBeDefined(); - }); -}); diff --git a/packages/test/src/streams/appendToStream-batch-append-flood.test.ts b/packages/test/src/streams/appendToStream-batch-append-flood.test.ts deleted file mode 100644 index 61d0c1a6..00000000 --- a/packages/test/src/streams/appendToStream-batch-append-flood.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ -import type { Duplex } from "stream"; - -import { - createTestNode, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; -import { EventStoreDBClient, jsonEvent } from "@eventstore/db-client"; - -describe("appendToStream - batch append - flood", () => { - const supported = matchServerVersion`>=21.10`; - - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - test("Can handle multiple appends within the same event loop", async () => { - const streamName = "batchFlood"; - const numberOfEvents = 10_000; - const value = "A".repeat(904); - - const oneKiloByteEvent = () => - jsonEvent({ - type: "AnyEventType", - data: { value }, - }); - - const requests = []; - for (let i = 0; i < numberOfEvents; i++) { - requests.push(client.appendToStream(streamName, oneKiloByteEvent())); - } - await Promise.all(requests); - - await client.dispose(); - }); - }); -}); diff --git a/packages/test/src/streams/appendToStream-batch-append.test.ts b/packages/test/src/streams/appendToStream-batch-append.test.ts deleted file mode 100644 index ad961b8f..00000000 --- a/packages/test/src/streams/appendToStream-batch-append.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ -import type { Duplex } from "stream"; - -import { - createTestNode, - jsonTestEvents, - matchServerVersion, - optionalDescribe, -} from "@test-utils"; -import { EventStoreDBClient } from "@eventstore/db-client"; -import { StreamsClient } from "@eventstore/db-client/generated/streams_grpc_pb"; - -describe("appendToStream - batch append", () => { - const supported = matchServerVersion`>=21.10`; - - const node = createTestNode(); - let client!: EventStoreDBClient; - let batchSpy!: jest.SpiedFunction; - let executeSpy!: jest.SpiedFunction; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - batchSpy = spyOn.call(client, "GRPCStreamCreator"); - executeSpy = spyOn.call(client, "execute"); - }); - - afterAll(async () => { - await node.down(); - }); - - afterEach(() => { - batchSpy.mockClear(); - executeSpy.mockClear(); - }); - - optionalDescribe(!supported)("Not Supported (<21.10)", () => { - test("Uses normal append", async () => { - const STREAM_NAME = "uses_normal_append"; - - const result = await client.appendToStream(STREAM_NAME, jsonTestEvents()); - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - expect(batchSpy).not.toHaveBeenCalled(); - expect(executeSpy).toHaveBeenCalledWith( - StreamsClient, - "appendToStream", - expect.any(Function) - ); - }); - }); - - optionalDescribe(supported)("Supported (>=21.10)", () => { - test("Uses batch append", async () => { - const STREAM_NAME = "uses_batch_append"; - - const result = await client.appendToStream(STREAM_NAME, jsonTestEvents()); - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - expect(batchSpy).toHaveBeenCalledWith( - StreamsClient, - "appendToStream", - expect.any(Function), - expect.any(WeakMap) - ); - }); - - test("Uses normal append if a credentials are passed", async () => { - const STREAM_NAME = "uses_normal_append_if_creds_are_passed"; - - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { credentials: { username: "admin", password: "changeit" } } - ); - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - expect(batchSpy).not.toHaveBeenCalled(); - expect(executeSpy).toHaveBeenCalledWith( - StreamsClient, - "appendToStream", - expect.any(Function) - ); - }); - - test("Batches events into batches", async () => { - await client.appendToStream("open_stream", jsonTestEvents()); - - const stream = await extractBatchStream.call( - client, - ...batchSpy.mock.calls[0] - ); - - const writeSpy = jest.spyOn(stream, "write"); - - const result = await client.appendToStream( - "small_batch_size", - jsonTestEvents(5_000), - { batchAppendSize: 1024 } - ); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - expect(writeSpy).toHaveBeenCalledTimes( - // (test event is 128 bytes) - // (size * event count) / requested batch size - (128 * 5_000) / 1024 - ); - }); - }); -}); - -/* eslint-disable @typescript-eslint/no-explicit-any */ - -function spyOn(this: EventStoreDBClient, method: string) { - return jest.spyOn(this, method as never) as any; -} - -function extractBatchStream( - this: EventStoreDBClient, - clientConstructor: any, - name: any, - _: any, - cache: any -): Promise { - return this.GRPCStreamCreator( - clientConstructor, - name, - () => { - throw "Creator shouldn't be called as it will take the client from the cache"; - }, - cache - )(); -} diff --git a/packages/test/src/streams/appendToStream-errors.test.ts b/packages/test/src/streams/appendToStream-errors.test.ts deleted file mode 100644 index 78d4ee7b..00000000 --- a/packages/test/src/streams/appendToStream-errors.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - jsonTestEvents, - matchServerVersion, - optionalTest, -} from "@test-utils"; - -import { - EventStoreDBClient, - WrongExpectedVersionError, - NO_STREAM, - StreamDeletedError, - MaxAppendSizeExceededError, - AccessDeniedError, - DeadlineExceededError, -} from "@eventstore/db-client"; - -describe("appendToStream - errors", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri, throwOnAppendFailure: true }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe.each([ - ["Batch append (>21.10)", matchServerVersion`>=21.10`], - ["Normal Append", true], - ])("%s", (prefix, supported) => { - optionalTest(supported)("WrongExpectedVersion", async () => { - const STREAM_NAME = `${prefix}_no_stream_here_but_there_is`; - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: "no_stream", - } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.expectedVersion).toBe(NO_STREAM); - expect(error.actualVersion).toBeGreaterThanOrEqual(1); - } - } - }); - - optionalTest(supported)("StreamDeleted", async () => { - const STREAM_NAME = `${prefix}_i_will_be_deleted`; - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - await client.tombstoneStream(STREAM_NAME); - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents() - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(StreamDeletedError); - - if (error instanceof StreamDeletedError) { - expect(error.streamName).toBe(STREAM_NAME); - } - } - }); - - optionalTest(supported)("AccessDenied", async () => { - const STREAM_NAME = `${prefix}_no_entry`; - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - await client.setStreamMetadata(STREAM_NAME, { - acl: { - writeRoles: ["some_user"], - }, - }); - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - credentials: { username: "AzureDiamond", password: "hunter2" }, - } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(AccessDeniedError); - } - }); - - optionalTest(supported)("DeadlineExceeded", async () => { - const STREAM_NAME = `${prefix}_deadline`; - - try { - await client.appendToStream(STREAM_NAME, jsonTestEvents(30_000), { - deadline: 1, - }); - expect("this point").toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(DeadlineExceededError); - } - }); - - optionalTest(supported)("MaximumAppendSizeExceeded", async () => { - const STREAM_NAME = `${prefix}_i_am_too_many`; - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(40_000) - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(MaxAppendSizeExceededError); - - if (error instanceof MaxAppendSizeExceededError) { - expect(typeof error.maxAppendSize).toBe("number"); - } - } - }); - }); -}); diff --git a/packages/test/src/streams/appendToStream.test.ts b/packages/test/src/streams/appendToStream.test.ts deleted file mode 100644 index b08a2215..00000000 --- a/packages/test/src/streams/appendToStream.test.ts +++ /dev/null @@ -1,620 +0,0 @@ -import { - binaryTestEvents, - collect, - createTestNode, - jsonTestEvents, -} from "@test-utils"; - -import { - EventStoreDBClient, - jsonEvent, - WrongExpectedVersionError, - ANY, - NO_STREAM, - STREAM_EXISTS, - binaryEvent, - BinaryEventType, - JSONEventType, -} from "@eventstore/db-client"; - -describe("appendToStream", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should successfully append to stream", () => { - test("json events", async () => { - const STREAM_NAME = "json_stream_name"; - - const result = await client.appendToStream(STREAM_NAME, jsonTestEvents()); - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - }); - - test("unicode string in json event", async () => { - const EVENT_TYPE = "TestEncode"; - const STREAM_NAME = "encode1"; - const KILLER = "CC ‐ 1830"; - - const client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream( - STREAM_NAME, - jsonEvent({ - type: EVENT_TYPE, - data: KILLER, - }) - ); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.data).toStrictEqual(KILLER); - count++; - } - expect(count).toEqual(1); - }); - - test("binary events", async () => { - const STREAM_NAME = "binary_stream_name"; - - const result = await client.appendToStream( - STREAM_NAME, - binaryTestEvents() - ); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - }); - - test("linkTo", async () => { - const LINK_FROM_STREAM_NAME = "link_from_test"; - const LINK_TO_STREAM_NAME = "link_to_test"; - - const LINK_REVISION = BigInt(2); - - await client.appendToStream(LINK_TO_STREAM_NAME, jsonTestEvents()); - - await client.appendToStream( - LINK_FROM_STREAM_NAME, - binaryEvent({ - type: "$>", - data: Buffer.from(`${LINK_REVISION}@${LINK_TO_STREAM_NAME}`), - }) - ); - - const [trueEvent] = await collect( - client.readStream(LINK_TO_STREAM_NAME, { - fromRevision: LINK_REVISION, - maxCount: 1, - }) - ); - - const [linkEvent] = await collect( - client.readStream(LINK_FROM_STREAM_NAME, { resolveLinkTos: true }) - ); - - expect(trueEvent.event).toBeDefined(); - expect(trueEvent.link).not.toBeDefined(); - expect(linkEvent.event).toBeDefined(); - expect(linkEvent.link).toBeDefined(); - - expect(trueEvent.event!).toEqual(linkEvent.event!); - }); - - describe("metadata", () => { - describe("json", () => { - const METADATA = { metaMessage: "How meta is this?" }; - - test("json events", async () => { - type E = JSONEventType< - "metadata-test", - { - message: string; - kind: string; - }, - { metaMessage: string } - >; - - const STREAM_NAME = "metadata_json_json"; - const event = jsonEvent({ - type: "metadata-test", - data: { - message: "the json message", - kind: "json", - }, - metadata: METADATA, - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeDefined(); - expect(event?.metadata.metaMessage).toMatch(METADATA.metaMessage); - count++; - } - expect(count).toEqual(1); - }); - - test("binary events", async () => { - type E = BinaryEventType<"metadata-test", { metaMessage: string }>; - - const STREAM_NAME = "metadata_json_binary"; - const event = binaryEvent({ - type: "metadata-test", - data: Buffer.from("the binary message"), - metadata: METADATA, - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeDefined(); - expect(event?.metadata.metaMessage).toMatch(METADATA.metaMessage); - count++; - } - expect(count).toEqual(1); - }); - }); - - describe("buffer", () => { - const MESSAGE = "How meta is this?"; - const METADATA = Buffer.from(MESSAGE); - - test("json events", async () => { - type E = JSONEventType< - "metadata-test", - { - message: string; - kind: string; - }, - Uint8Array - >; - - const STREAM_NAME = "metadata_buffer_json"; - const event = jsonEvent({ - type: "metadata-test", - data: { - message: "the json message", - kind: "json", - }, - metadata: METADATA, - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeDefined(); - const metaMessage = Buffer.from(event!.metadata).toString("binary"); - expect(metaMessage).toMatch(MESSAGE); - count++; - } - expect(count).toEqual(1); - }); - - test("binary events", async () => { - type E = BinaryEventType<"metadata-test", Uint8Array>; - - const STREAM_NAME = "metadata_buffer_binary"; - const event = binaryEvent({ - type: "metadata-test", - data: Buffer.from("the binary message"), - metadata: METADATA, - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeDefined(); - const metaMessage = Buffer.from(event!.metadata).toString("binary"); - expect(metaMessage).toMatch(MESSAGE); - count++; - } - expect(count).toEqual(1); - }); - }); - - describe("Uint8Array", () => { - const MESSAGE = "How Uint8Array is this?"; - const METADATA = Uint8Array.from(Buffer.from(MESSAGE)); - - test("json events", async () => { - type E = JSONEventType< - "metadata-test", - { - message: "the json message"; - kind: "json"; - }, - Uint8Array - >; - - const STREAM_NAME = "metadata_Uint8Array_json"; - const event = jsonEvent({ - type: "metadata-test", - data: { - message: "the json message", - kind: "json", - }, - metadata: METADATA, - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeDefined(); - const metaMessage = Buffer.from(event!.metadata).toString("binary"); - expect(metaMessage).toMatch(MESSAGE); - count++; - } - expect(count).toEqual(1); - }); - - test("binary events", async () => { - type E = BinaryEventType<"metadata-test", Uint8Array>; - - const STREAM_NAME = "metadata_Uint8Array_binary"; - const event = binaryEvent({ - type: "metadata-test", - data: Buffer.from("the binary message"), - metadata: METADATA, - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeDefined(); - const metaMessage = Buffer.from(event!.metadata).toString("binary"); - expect(metaMessage).toMatch(MESSAGE); - count++; - } - expect(count).toEqual(1); - }); - }); - - describe("undefined", () => { - test("json events", async () => { - const STREAM_NAME = "metadata_undefined_json"; - const event = jsonEvent({ - type: "metadata-test", - data: { - message: "the json message", - kind: "json", - }, - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeUndefined(); - count++; - } - expect(count).toEqual(1); - }); - - test("binary events", async () => { - const STREAM_NAME = "metadata_undefined_binary"; - const event = binaryEvent({ - type: "metadata-test", - data: Buffer.from("the binary message"), - }); - - const result = await client.appendToStream(STREAM_NAME, event); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - - let count = 0; - for await (const { event } of client.readStream(STREAM_NAME, { - maxCount: 1, - })) { - expect(event?.metadata).toBeUndefined(); - count++; - } - expect(count).toEqual(1); - }); - }); - }); - - describe("expected revision", () => { - describe(ANY, () => { - test("succeeds", async () => { - const STREAM_NAME = "any_stream_doesnt_matter"; - - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: ANY, - } - ); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - }); - }); - - describe(NO_STREAM, () => { - test("succeeds", async () => { - const STREAM_NAME = "no_stream_here"; - - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: NO_STREAM, - } - ); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - }); - - test("fails", async () => { - const STREAM_NAME = "no_stream_here_but_there_is"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: "no_stream", - } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.expectedVersion).toBe(NO_STREAM); - expect(error.actualVersion).toBeGreaterThanOrEqual(1); - } - } - }); - }); - - describe(STREAM_EXISTS, () => { - test("succeeds", async () => { - const STREAM_NAME = "stream_should_exist"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: STREAM_EXISTS, - } - ); - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - }); - - test("fails", async () => { - const STREAM_NAME = "stream_should_exist_but_doesnt"; - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: STREAM_EXISTS, - } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.expectedVersion).toBe(STREAM_EXISTS); - expect(error.actualVersion).toBe(NO_STREAM); - } - } - }); - }); - - describe("exact version", () => { - test("succeeds", async () => { - const STREAM_NAME = "stream_should_be_at_nextExpectedRevision"; - - const { nextExpectedRevision } = await client.appendToStream( - STREAM_NAME, - jsonTestEvents() - ); - - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: nextExpectedRevision, - } - ); - - expect(result).toBeDefined(); - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(0); - }); - - describe("fails", () => { - test("no stream", async () => { - const STREAM_NAME = "stream_should_be_at_revision_but_doesnt_exist"; - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { expectedRevision: BigInt(1) } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.expectedVersion).toBe(BigInt(1)); - expect(error.actualVersion).toBe(NO_STREAM); - } - } - }); - - test("wrong version", async () => { - const STREAM_NAME = "stream_should_be_at_revision_but_isnt"; - - const { nextExpectedRevision } = await client.appendToStream( - STREAM_NAME, - jsonTestEvents() - ); - - try { - const result = await client.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: nextExpectedRevision + BigInt(1), - } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.expectedVersion).toBe( - nextExpectedRevision + BigInt(1) - ); - expect(error.actualVersion).toBe(nextExpectedRevision); - } - } - }); - }); - }); - }); - }); - - describe("throwOnAppendFailure", () => { - test("throws on true", async () => { - const throwingClient = new EventStoreDBClient( - { endpoint: node.uri, throwOnAppendFailure: true }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const STREAM_NAME = "throwing__no_stream_here_but_there_is"; - - await throwingClient.appendToStream(STREAM_NAME, jsonTestEvents()); - - try { - const result = await throwingClient.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: "no_stream", - } - ); - - expect(result).toBe("unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM_NAME); - expect(error.expectedVersion).toBe(NO_STREAM); - expect(error.actualVersion).toBeGreaterThanOrEqual(1); - } - } - }); - - test("returns failure result on false", async () => { - const nonThrowingClient = new EventStoreDBClient( - { endpoint: node.uri, throwOnAppendFailure: false }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - const STREAM_NAME = "no_throwing__no_stream_here_but_there_is"; - - await nonThrowingClient.appendToStream(STREAM_NAME, jsonTestEvents()); - - const result = await nonThrowingClient.appendToStream( - STREAM_NAME, - jsonTestEvents(), - { - expectedRevision: "no_stream", - } - ); - - expect(result.success).toBe(false); - - if (result.success) { - expect(result).toBe("unreachable"); - } else { - expect(result.nextExpectedRevision).toBeGreaterThanOrEqual(1); - } - }); - }); -}); diff --git a/packages/test/src/streams/deleteStream.test.ts b/packages/test/src/streams/deleteStream.test.ts deleted file mode 100644 index d8d00ae9..00000000 --- a/packages/test/src/streams/deleteStream.test.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { collect, createTestNode, jsonTestEvents } from "@test-utils"; -import { - EventStoreDBClient, - WrongExpectedVersionError, - NO_STREAM, - StreamNotFoundError, - BACKWARDS, -} from "@eventstore/db-client"; - -describe("deleteStream", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should successfully delete a stream", () => { - describe("any revision", () => { - const ANY_REVISION_STREAM = "any_revision_stream"; - - beforeAll(async () => { - await client.appendToStream(ANY_REVISION_STREAM, jsonTestEvents(4)); - }); - - it("succeeds", async () => { - const result = await client.deleteStream(ANY_REVISION_STREAM); - expect(result).toBeDefined(); - - await expect( - collect(client.readStream(ANY_REVISION_STREAM, { maxCount: 10 })) - ).rejects.toThrowError(StreamNotFoundError); - }); - }); - - describe("expected revision", () => { - describe("exact", () => { - const STREAM = "expected_revision_stream_exact"; - - beforeAll(async () => { - await client.appendToStream(STREAM, jsonTestEvents(4)); - }); - - it("fails", async () => { - try { - const result = await client.deleteStream(STREAM, { - expectedRevision: BigInt(2), - }); - - expect(result).toBe("Unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM); - expect(error.expectedVersion).toBe(BigInt(2)); - } - } - }); - - it("succeeds", async () => { - const events = await collect( - client.readStream(STREAM, { - maxCount: 1, - direction: BACKWARDS, - fromRevision: "end", - }) - ); - - const expectedRevision = events[0].event!.revision; - - const result = await client.deleteStream(STREAM, { - expectedRevision, - }); - - expect(result).toBeDefined(); - - await expect( - collect(client.readStream(STREAM, { maxCount: 1 })) - ).rejects.toThrowError(StreamNotFoundError); - }); - }); - - describe(NO_STREAM, () => { - it("fails if stream exists", async () => { - const STREAM = "i_exist_hopefully"; - - await client.appendToStream(STREAM, jsonTestEvents(4)); - - try { - const result = await client.deleteStream(STREAM, { - expectedRevision: NO_STREAM, - }); - - expect(result).toBe("Unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM); - } - } - }); - - it("fails if stream doesn't exist", async () => { - const NOT_A_STREAM = "expected_revision_stream_no_stream"; - - try { - const result = await client.deleteStream(NOT_A_STREAM, { - expectedRevision: NO_STREAM, - }); - - // Before https://github.com/EventStore/EventStore/pull/3154 this should pass. - expect(result).toBeDefined(); - } catch (error) { - // After https://github.com/EventStore/EventStore/pull/3154 this will throw an error. - expect(error).toBeInstanceOf(WrongExpectedVersionError); - - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(NOT_A_STREAM); - } - } - }); - - it("succeeds if stream implicityly exists", async () => { - const IMPLICITLY_A_STREAM = "i_exist_implicitly"; - - await client.setStreamMetadata(IMPLICITLY_A_STREAM, { - cacheControl: 10, - }); - - const result = await client.deleteStream(IMPLICITLY_A_STREAM, { - expectedRevision: NO_STREAM, - }); - - expect(result).toBeDefined(); - }); - }); - }); - }); -}); diff --git a/packages/test/src/streams/getStreamMetadata.test.ts b/packages/test/src/streams/getStreamMetadata.test.ts deleted file mode 100644 index 66145cdc..00000000 --- a/packages/test/src/streams/getStreamMetadata.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { createTestNode, jsonTestEvents } from "@test-utils"; -import { EventStoreDBClient, StreamMetadata } from "@eventstore/db-client"; - -describe("getStreamMetadata", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should return empty metadata", () => { - test("any stream", async () => { - const STREAM_NAME = "anything"; - await client.getStreamMetadata(STREAM_NAME); - }); - - test("written to stream", async () => { - const STREAM_NAME = "test_stream_name"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents()); - - const metadata = await client.getStreamMetadata(STREAM_NAME); - - expect(metadata).toEqual({ streamName: STREAM_NAME }); - }); - }); - - describe("should return the written metadata", () => { - test("all keys metadata", async () => { - const STREAM_NAME = "all_keys_stream_name"; - - const metadata: StreamMetadata = { - maxAge: 2, - cacheControl: 15, - truncateBefore: 1, - maxCount: 12, - acl: { - readRoles: ["admin"], - writeRoles: ["admin"], - deleteRoles: ["admin"], - metaReadRoles: ["admin"], - metaWriteRoles: ["admin"], - }, - }; - - await client.setStreamMetadata(STREAM_NAME, metadata); - - const metadataResult = await client.getStreamMetadata(STREAM_NAME); - - expect(metadataResult.streamName).toEqual(STREAM_NAME); - expect(metadataResult.metadata).toEqual(metadata); - }); - - test("custom keys metadata", async () => { - const STREAM_NAME = "custom_keys_stream_name"; - - type CustomMetadata = { - turkey: string; - }; - - const metadata: StreamMetadata = { - maxCount: 12, - turkey: "🦃", - }; - - await client.setStreamMetadata(STREAM_NAME, metadata); - - const metadataResult = await client.getStreamMetadata(STREAM_NAME); - - expect(metadataResult.streamName).toEqual(STREAM_NAME); - expect(metadataResult.metadata).toEqual(metadata); - - const metadata2: StreamMetadata = { - maxCount: 11, - turkey: "🍗", - }; - - await client.setStreamMetadata(STREAM_NAME, metadata2); - - const metadataResult2 = await client.getStreamMetadata(STREAM_NAME); - - expect(metadataResult2.streamName).toEqual(STREAM_NAME); - expect(metadataResult2.metadata).toEqual(metadata2); - }); - }); -}); diff --git a/packages/test/src/streams/readAll.test.ts b/packages/test/src/streams/readAll.test.ts deleted file mode 100644 index 4da3707c..00000000 --- a/packages/test/src/streams/readAll.test.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { collect, createTestNode, delay, jsonTestEvents } from "@test-utils"; -import { - EventStoreDBClient, - BACKWARDS, - END, - AllStreamResolvedEvent, - jsonEvent, - AllStreamBinaryRecordedEvent, - LinkEvent, -} from "@eventstore/db-client"; - -describe("readAll", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - const STREAM_NAME_A = "stream_name_a"; - const STREAM_NAME_B = "stream_name_b"; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream(STREAM_NAME_A, jsonTestEvents()); - await client.appendToStream(STREAM_NAME_B, jsonTestEvents()); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should successfully read from $all", () => { - test("from start", async () => { - let count = 0; - const notSystemStreams = []; - - for await (const { event } of client.readAll()) { - count++; - - if (event && !event.streamId.startsWith("$")) { - notSystemStreams.push(event.streamId); - } - } - - expect(count).toBeGreaterThan(8); - expect(notSystemStreams.length).toEqual(8); - expect(notSystemStreams[0]).toBe(STREAM_NAME_A); - }); - - test("from position", async () => { - let eventToExtract!: AllStreamResolvedEvent; - - for await (const event of client.readAll({ maxCount: 3 })) { - eventToExtract = event; - } - const { position } = eventToExtract!.event!; - - let extracted!: AllStreamResolvedEvent; - for await (const event of client.readAll({ - maxCount: 1, - fromPosition: position, - })) { - extracted = event; - } - - expect(extracted).toEqual(eventToExtract); - }); - - test("backwards from end", async () => { - let count = 0; - const notSystemStreams = []; - - for await (const { event } of client.readAll({ - direction: BACKWARDS, - fromPosition: END, - })) { - count++; - - if (event && !event.streamId.startsWith("$")) { - notSystemStreams.push(event.streamId); - } - } - - expect(count).toBeGreaterThan(8); - expect(notSystemStreams.length).toEqual(8); - expect(notSystemStreams[0]).toBe(STREAM_NAME_B); - }); - - test("maxCount", async () => { - let count = 0; - - for await (const _ of client.readAll({ maxCount: 2 })) { - count++; - } - - expect(count).toBe(2); - }); - - test("resolve link tos", async () => { - const FROM_STREAM_NAME = "link-from-stream"; - - await client.enableProjection("$by_category"); - - await client.createProjection( - "projection", - `fromStream("${FROM_STREAM_NAME}").when({ - $any: function(state, ev) { - linkTo('a-' + ev.data.some, ev) - } - });`, - { emitEnabled: true } - ); - - // Append an event that will be linked - - await client.appendToStream( - FROM_STREAM_NAME, - jsonEvent({ type: "linky", data: { some: "thing" } }) - ); - - await delay(1000); - - // by default, resolveLinkTos should be false - const noResolveLink = client.readAll(); - - let noResolveEvent!: AllStreamResolvedEvent; - - for await (const event of noResolveLink) { - if (event.event?.type !== "$>") continue; - noResolveEvent = event; - break; - } - - // We found a link event - expect(noResolveEvent).toBeDefined(); - - // link event - expect(noResolveEvent.event).toBeDefined(); - expect(noResolveEvent.event?.type).toBe("$>"); - - const doResolveLink = client.readAll({ - maxCount: 1, - resolveLinkTos: true, - fromPosition: noResolveEvent.event!.position, - }); - const [doResolveEvent] = await collect(doResolveLink); - - // resolved event - expect(doResolveEvent.event).toBeDefined(); - expect(doResolveEvent.event?.type).toBe("linky"); - - // link event - expect(doResolveEvent.link).toBeDefined(); - expect(doResolveEvent.link?.type).toBe("$>"); - - expect(doResolveEvent.event?.id).toBe( - (noResolveEvent.event as AllStreamBinaryRecordedEvent)! - .metadata!.$causedBy - ); - expect(doResolveEvent.event?.id).toBe( - doResolveEvent.link!.metadata.$causedBy - ); - }); - }); -}); diff --git a/packages/test/src/streams/readStream.test.ts b/packages/test/src/streams/readStream.test.ts index 1270cd28..0d3c20fc 100644 --- a/packages/test/src/streams/readStream.test.ts +++ b/packages/test/src/streams/readStream.test.ts @@ -58,7 +58,7 @@ describe("readStream", () => { test("json event", async () => { let resolvedEvent!: ResolvedEvent; - for await (const event of client.readStream(STREAM_NAME, { + for await (const event of await client.readStream(STREAM_NAME, { maxCount: 1, fromRevision: BigInt(1), })) { @@ -78,7 +78,7 @@ describe("readStream", () => { test("binary event", async () => { let resolvedEvent!: ResolvedEvent; - for await (const event of client.readStream(STREAM_NAME, { + for await (const event of await client.readStream(STREAM_NAME, { maxCount: 1, fromRevision: BigInt(5), })) { @@ -98,7 +98,7 @@ describe("readStream", () => { test("from start", async () => { let count = 0; - for await (const _ of client.readStream(STREAM_NAME)) { + for await (const _ of await client.readStream(STREAM_NAME)) { count++; } @@ -108,7 +108,7 @@ describe("readStream", () => { test("from revision", async () => { let count = 0; - for await (const _ of client.readStream(STREAM_NAME, { + for await (const _ of await client.readStream(STREAM_NAME, { fromRevision: BigInt(1), })) { count++; @@ -120,7 +120,7 @@ describe("readStream", () => { test("backwards from end", async () => { let count = 0; - for await (const _ of client.readStream(STREAM_NAME, { + for await (const _ of await client.readStream(STREAM_NAME, { direction: BACKWARDS, fromRevision: END, })) { @@ -133,7 +133,7 @@ describe("readStream", () => { test("backwards from revision", async () => { let count = 0; - for await (const _ of client.readStream(STREAM_NAME, { + for await (const _ of await client.readStream(STREAM_NAME, { direction: BACKWARDS, fromRevision: BigInt(1), })) { @@ -146,7 +146,7 @@ describe("readStream", () => { test("maxCount", async () => { let count = 0; - for await (const _ of client.readStream(STREAM_NAME, { maxCount: 2 })) { + for await (const _ of await client.readStream(STREAM_NAME, { maxCount: 2 })) { count++; } @@ -178,7 +178,7 @@ describe("readStream", () => { await delay(1000); // by default, resolveLinkTos should be false - const noResolveLink = client.readStream("a-thing", { + const noResolveLink = await client.readStream("a-thing", { maxCount: 1, }); const [noResolveEvent] = await collect(noResolveLink); @@ -187,7 +187,7 @@ describe("readStream", () => { expect(noResolveEvent.event).toBeDefined(); expect(noResolveEvent.event?.type).toBe("$>"); - const doResolveLink = client.readStream("a-thing", { + const doResolveLink = await client.readStream("a-thing", { maxCount: 1, resolveLinkTos: true, }); @@ -215,7 +215,7 @@ describe("readStream", () => { const NO_STREAM_NAME = "this_is_not_a_stream"; try { - for await (const e of client.readStream(NO_STREAM_NAME)) { + for await (const e of await client.readStream(NO_STREAM_NAME)) { expect(e).toBe("UNREACHABLE"); } } catch (error) { @@ -230,7 +230,7 @@ describe("readStream", () => { test("stream revision invalid argument lower bound", async () => { let count = 0; try { - for await (const e of client.readStream(STREAM_NAME, { + for await (const e of await client.readStream(STREAM_NAME, { direction: BACKWARDS, fromRevision: BigInt(-1), })) { @@ -244,7 +244,7 @@ describe("readStream", () => { test("stream revision invalid argument upper bound", async () => { let count = 0; try { - for await (const e of client.readStream(STREAM_NAME, { + for await (const e of await client.readStream(STREAM_NAME, { direction: BACKWARDS, fromRevision: BigInt("18446744073709551616"), })) { @@ -264,7 +264,7 @@ describe("readStream", () => { expect(result).toBeDefined(); try { - for await (const event of client.readStream(DELETE_STREAM_NAME, { + for await (const event of await client.readStream(DELETE_STREAM_NAME, { maxCount: 10, })) { expect(event).toBe("Unreachable"); @@ -282,7 +282,7 @@ describe("readStream", () => { optionalDescribe(supported)("Supported (>=22.6.0)", () => { test("populates log position", async () => { const [resolvedEvent] = await collect( - client.readStream(STREAM_NAME, { + await client.readStream(STREAM_NAME, { maxCount: 1, fromRevision: END, direction: BACKWARDS, diff --git a/packages/test/src/streams/setStreamMetadata.test.ts b/packages/test/src/streams/setStreamMetadata.test.ts deleted file mode 100644 index bd9899c1..00000000 --- a/packages/test/src/streams/setStreamMetadata.test.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { createTestNode, jsonTestEvents } from "@test-utils"; -import { - EventStoreDBClient, - StreamMetadata, - START, -} from "@eventstore/db-client"; - -describe("setStreamMetadata", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should successfully set metadata of stream", () => { - test("partial metadata", async () => { - const STREAM_NAME = "partial_stream_name"; - - const metadata: StreamMetadata = { - maxAge: 2, - }; - - await client.setStreamMetadata(STREAM_NAME, metadata); - - const { metadata: readMetadata } = await client.getStreamMetadata( - STREAM_NAME - ); - - expect(readMetadata).toEqual(metadata); - }); - - test("all keys metadata", async () => { - const STREAM_NAME = "all_keys_stream_name"; - - const metadata: StreamMetadata = { - maxAge: 2, - cacheControl: 15, - truncateBefore: 1, - maxCount: 12, - acl: { - readRoles: ["admin"], - writeRoles: ["admin"], - deleteRoles: ["admin"], - metaReadRoles: ["admin"], - metaWriteRoles: ["admin"], - }, - }; - - await client.setStreamMetadata(STREAM_NAME, metadata); - - const { metadata: readMetadata } = await client.getStreamMetadata( - STREAM_NAME - ); - - expect(readMetadata).toEqual(metadata); - }); - - test("extra keys", async () => { - const STREAM_NAME = "test_stream_name"; - - type CustomStreamMetadata = { - kitten: string; - }; - - const metadata: StreamMetadata = { - maxAge: 2, - kitten: "😾", - }; - - await client.setStreamMetadata( - STREAM_NAME, - metadata - ); - - const { metadata: readMetadata } = - await client.getStreamMetadata(STREAM_NAME); - - expect(readMetadata).toEqual(metadata); - // key is accessible on type - expect(readMetadata!.kitten).toBeDefined(); - // @ts-expect-error `cat` doesnt exist on `CustomStreamMetadata`, so this errors - expect(readMetadata.cat).not.toBeDefined(); - }); - - test("ignore unknown keys in acl", async () => { - const STREAM_NAME = "unknown_keys_acl"; - - const warnSpy = jest.spyOn(console, "warn").mockImplementation(); - - const metadata: StreamMetadata = { - acl: { - deleteRoles: ["admin"], - // @ts-expect-error `chicken` doesnt exist on `acl` - chicken: "🐔", - }, - }; - - await client.setStreamMetadata(STREAM_NAME, metadata); - - const { metadata: readMetadata } = await client.getStreamMetadata( - STREAM_NAME - ); - - expect(readMetadata!.acl).toEqual({ - deleteRoles: ["admin"], - }); - - expect(warnSpy.mock.calls).toHaveLength(1); - warnSpy.mockRestore(); - }); - - test("metadata goes to the right place", async () => { - const STREAM_NAME = "the_right_place"; - - const metadata: StreamMetadata = { - // only two events in this stream - maxCount: 2, - }; - - await client.setStreamMetadata(STREAM_NAME, metadata); - await client.appendToStream(STREAM_NAME, jsonTestEvents(20)); - - let count = 0; - for await (const _ of client.readStream(STREAM_NAME, { - fromRevision: START, - maxCount: 200, - })) { - count++; - } - - expect(count).toBe(2); - }); - - describe("disallows non integer numbers", () => { - test.each(["maxAge", "truncateBefore", "cacheControl", "maxCount"])( - "for %s", - async (key) => { - const STREAM_NAME = "err"; - - await expect( - client.setStreamMetadata(STREAM_NAME, { [key]: 2.5 }) - ).rejects.toThrowError( - `Invalid stream metadata: "${key}" must be an integer.` - ); - } - ); - }); - }); -}); diff --git a/packages/test/src/streams/subscribeToAll-filters.test.ts b/packages/test/src/streams/subscribeToAll-filters.test.ts deleted file mode 100644 index ca6ccd5f..00000000 --- a/packages/test/src/streams/subscribeToAll-filters.test.ts +++ /dev/null @@ -1,293 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { - createTestNode, - Defer, - delay, - jsonTestEvents, - matchServerVersion, - optionalTest, -} from "@test-utils"; -import { - EventStoreDBClient, - jsonEvent, - ResolvedEvent, - streamNameFilter, - START, - eventTypeFilter, - excludeSystemEvents, - AllStreamSubscription, - Position, -} from "@eventstore/db-client"; - -describe("subscribeToAll", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const STREAM_NAME_A = "stream_name_a"; - const STREAM_NAME_B = "stream_name_b"; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream(STREAM_NAME_A, jsonTestEvents(4)); - await client.appendToStream(STREAM_NAME_B, jsonTestEvents(4)); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should accept a filter", () => { - describe("streamName", () => { - test.each` - name | filter | streamName - ${"prefixes"} | ${streamNameFilter({ prefixes: ["prefix_filter_streamname"] })} | ${(k: string) => `prefix_filter_streamname_${k}`} - ${"regex"} | ${streamNameFilter({ regex: "^[0-9]*_regex_filter_streamname_" })} | ${(k: string) => `${Math.floor(Math.random() * 1000)}_regex_filter_streamname_${k}`} - `("$name", async ({ filter, streamName }) => { - const STREAM_NAME_A = streamName("a"); - const STREAM_NAME_B = streamName("b"); - - const FINISH_TEST = "finish_streamName_filter_test"; - const doSomething = jest.fn(); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - await client.appendToStream(STREAM_NAME_A, jsonTestEvents(8)); - await client.appendToStream(STREAM_NAME_B, jsonTestEvents(8)); - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(8), - finishEvent, - ]); - - const subscription = client.subscribeToAll({ - fromPosition: START, - filter, - }); - - for await (const event of subscription) { - doSomething(event); - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething).toBeCalledTimes( - 8 + // a - 8 + // b - 8 + // a - 1 // finish - ); - }); - }); - - describe("eventType", () => { - test.each` - name | filter | eventType - ${"prefixes"} | ${eventTypeFilter({ prefixes: ["prefix_filter_eventType"] })} | ${(k: string) => `prefix_filter_eventType_${k}`} - ${"regex"} | ${eventTypeFilter({ regex: "^[0-9]*_regex_filter_eventType_[A-z]*$" })} | ${(k: string) => `${Math.floor(Math.random() * 1000)}_regex_filter_eventType_${k}`} - `("$name", async ({ name, filter, eventType }) => { - const STREAM_NAME_A = `filter_eventType_${name}_a`; - const STREAM_NAME_B = `filter_eventType_${name}_b`; - - const FINISH_TEST = eventType("finish"); - const doSomething = jest.fn(); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - await client.appendToStream( - STREAM_NAME_A, - jsonTestEvents(8, eventType("a")) - ); - await client.appendToStream( - STREAM_NAME_B, - jsonTestEvents(8, eventType("b")) - ); - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(8, eventType("c")), - finishEvent, - ]); - - const subscription = client.subscribeToAll({ - fromPosition: START, - filter, - }); - - for await (const event of subscription) { - doSomething(event); - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething).toBeCalledTimes( - 8 + // a - 8 + // b - 8 + // a - 1 // finish - ); - }); - }); - - test("excludeSystemEvents", async () => { - const STREAM_NAME = "exclude_system_events_stream"; - const FINISH_TEST = "finish_exclude_system_events"; - const doSomething = jest.fn(); - const doSomethingWithNonSystemEvent = jest.fn(); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - client.appendToStream(STREAM_NAME, [...jsonTestEvents(8), finishEvent]); - - const subscription = client.subscribeToAll({ - fromPosition: START, - filter: excludeSystemEvents(), - }); - - for await (const event of subscription) { - doSomething(event); - - if (!event.event?.type.startsWith("$")) { - doSomethingWithNonSystemEvent(event); - } - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - // We run from the start, so could be more - expect(doSomething.mock.calls.length).toBeGreaterThanOrEqual(9); - expect(doSomethingWithNonSystemEvent).toBeCalledTimes( - doSomething.mock.calls.length - ); - }); - - // checkpoints behaviour was fixed in - // https://github.com/EventStore/EventStore/pull/2608 - optionalTest(matchServerVersion`>=21.10`)("checkpoints", async () => { - const defer = new Defer(); - const FINISH_TEST = "checkpoints-finish"; - const MARKER_EVENT = "marker_event"; - - const appendResult = await client.appendToStream( - STREAM_NAME_B, - jsonEvent({ - type: MARKER_EVENT, - data: { message: "mark my words" }, - }) - ); - - await client.appendToStream(STREAM_NAME_A, jsonTestEvents(8)); - - const events: ResolvedEvent[] = []; - const checkpoints: Position[] = []; - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(defer.resolve); - const onEvent = jest.fn((event: ResolvedEvent) => { - events.push(event); - - if (event.event?.type === FINISH_TEST) { - subscription.unsubscribe(); - } - }); - - let active = false; - - const checkpointReached = jest.fn( - async (_: AllStreamSubscription, position: Position) => { - checkpoints.push(position); - - active = true; - await delay(100); - active = false; - } - ); - - const confirmWaitingForCheckpointReached = jest.fn(() => { - expect(active).toBe(false); - }); - - const subscription = client - .subscribeToAll({ - fromPosition: appendResult.position, - filter: excludeSystemEvents({ - checkpointInterval: 2, - checkpointReached, - }), - }) - .on("error", onError) - .on("data", onEvent) - .on("data", confirmWaitingForCheckpointReached) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("end", onEnd); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - for (let i = 0; i < 18; i++) { - await client.appendToStream(STREAM_NAME_A, jsonTestEvents(20)); - await delay(10); - } - - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(15), - finishEvent, - ]); - - await defer.promise; - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - - // 8 before subscribed, 376 after subscribed - expect(onEvent).toBeCalledTimes(384); - expect(confirmWaitingForCheckpointReached).toBeCalledTimes(384); - expect(events.length).toBe(384); - - // 384 / (32 * 2 (checkpointInterval) ) = 6; - expect(checkpointReached.mock.calls.length).toBeGreaterThanOrEqual(6); - // sometimes we can have a catch up checkpoint - expect(checkpointReached.mock.calls.length).toBeLessThanOrEqual(7); - - for (const { commit, prepare } of checkpoints) { - expect(typeof commit).toBe("bigint"); - expect(typeof prepare).toBe("bigint"); - } - }); - }); -}); diff --git a/packages/test/src/streams/subscribeToAll.test.ts b/packages/test/src/streams/subscribeToAll.test.ts deleted file mode 100644 index 621ad06f..00000000 --- a/packages/test/src/streams/subscribeToAll.test.ts +++ /dev/null @@ -1,474 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { pipeline, Writable, Readable, finished } from "stream"; -import { promisify } from "util"; - -import { - createTestNode, - Defer, - delay, - jsonTestEvents, - matchServerVersion, - TestEventData, -} from "@test-utils"; -import { - EventStoreDBClient, - jsonEvent, - ResolvedEvent, - END, -} from "@eventstore/db-client"; - -const asyncPipeline = promisify(pipeline); - -describe("subscribeToAll", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const STREAM_NAME_A = "stream_name_a"; - const STREAM_NAME_B = "stream_name_b"; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { - rootCertificate: node.certs.root, - }, - { username: "admin", password: "changeit" } - ); - - await client.appendToStream(STREAM_NAME_A, jsonTestEvents(4)); - await client.appendToStream(STREAM_NAME_B, jsonTestEvents(4)); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should subscribe to $all", () => { - test("from start", async () => { - const defer = new Defer(); - const FINISH_TEST = "from-start-finish"; - - const events: ResolvedEvent[] = []; - const filteredEvents: ResolvedEvent[] = []; - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(defer.resolve); - - const onEvent = jest.fn(async (event: ResolvedEvent) => { - events.push(event); - - if (!event.event?.type.startsWith("$")) { - filteredEvents.push(event); - } - - if (event.event?.type === FINISH_TEST) { - await delay(500); - subscription.unsubscribe(); - } - }); - - const caughtUp = jest.fn(); - - const subscription = client - .subscribeToAll() - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("caughtUp", caughtUp) - .on("end", onEnd); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(3), - finishEvent, - ]); - - await defer.promise; - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - expect(onEvent).toHaveBeenCalled(); - - if (matchServerVersion`>=23.10`) { - expect(caughtUp).toBeCalledTimes(1); - } - - // 8 set up events, 4 after subscribed - expect(filteredEvents.length).toBe(12); - // plus system events - expect(events.length).toBeGreaterThan(12); - }); - - test("from end", async () => { - const defer = new Defer(); - const FINISH_TEST = "from-end-finish"; - - const events: ResolvedEvent[] = []; - const filteredEvents: ResolvedEvent[] = []; - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(defer.resolve); - const onEvent = jest.fn((event: ResolvedEvent) => { - events.push(event); - - if (!event.event?.type.startsWith("$")) { - filteredEvents.push(event); - } - - if (event.event?.type === FINISH_TEST) { - subscription.unsubscribe(); - } - }); - - const onCaughtUp = jest.fn(); - - const subscription = client - .subscribeToAll({ fromPosition: END }) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("caughtUp", onCaughtUp) - .on("end", onEnd); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - await delay(500); - - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(3), - finishEvent, - ]); - - await defer.promise; - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - expect(onEvent).toHaveBeenCalled(); - - if (matchServerVersion`>=23.10`) { - expect(onCaughtUp).toBeCalledTimes(1); - } - - // only 4 after subscribed - expect(filteredEvents.length).toBe(4); - // plus system events (if any) - expect(events.length).toBeGreaterThanOrEqual(4); - }); - - test("from position", async () => { - const defer = new Defer(); - const FINISH_TEST = "from-position-finish"; - const MARKER_EVENT = "marker_event"; - - const appendResult = await client.appendToStream( - STREAM_NAME_B, - jsonEvent({ - type: MARKER_EVENT, - data: { message: "mark my words" }, - }) - ); - - await client.appendToStream(STREAM_NAME_A, jsonTestEvents(3)); - - const events: ResolvedEvent[] = []; - const filteredEvents: ResolvedEvent[] = []; - - const onError = jest.fn((error) => { - defer.reject(error); - }); - const onClose = jest.fn(); - const onConfirmation = jest.fn(); - const onEnd = jest.fn(defer.resolve); - const onEvent = jest.fn(async (event: ResolvedEvent) => { - events.push(event); - - if (!event.event?.type.startsWith("$")) { - filteredEvents.push(event); - } - - if (event.event?.type === FINISH_TEST) { - await delay(500); - subscription.unsubscribe(); - } - }); - const onCaughtUp = jest.fn(); - - const subscription = client - .subscribeToAll({ - fromPosition: appendResult.position, - }) - .on("error", onError) - .on("data", onEvent) - .on("close", onClose) - .on("confirmation", onConfirmation) - .on("caughtUp", onCaughtUp) - .on("end", onEnd); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - await client.appendToStream(STREAM_NAME_A, [ - ...jsonTestEvents(3), - finishEvent, - ]); - - await defer.promise; - - expect(onError).not.toBeCalled(); - expect(onConfirmation).toBeCalledTimes(1); - expect(onEvent).toHaveBeenCalled(); - if (matchServerVersion`>=23.10`) { - expect(onCaughtUp).toBeCalledTimes(1); - } - - // 3 before subscribed, 4 after subscribed - expect(filteredEvents.length).toBe(7); - // plus system events (if any) - expect(events.length).toBeGreaterThanOrEqual(7); - }); - }); - - describe("should return a readable stream", () => { - test("async iterator", async () => { - const STREAM_NAME = "async_iter_sync_fun"; - const FINISH_TEST = "finish_async_iterator_sync_fun"; - const MARKER_EVENT = "async_iter_sync_fun_marker"; - const doSomething = jest.fn(); - const doSomethingElse = jest.fn(); - - const markerEvent = jsonEvent({ - type: MARKER_EVENT, - data: { - message: "mark", - }, - }); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - const appendResult = await client.appendToStream( - STREAM_NAME_B, - markerEvent - ); - - client.appendToStream(STREAM_NAME, [...jsonTestEvents(8), finishEvent]); - - const subscription = client.subscribeToAll({ - fromPosition: appendResult.position, - }); - - for await (const event of subscription) { - doSomething(event); - - if (!event.event?.type.startsWith("$")) { - doSomethingElse(event); - } - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething).toBeCalled(); - expect(doSomethingElse).toBeCalledTimes(9); - }); - - test("async iterator with async function", async () => { - const STREAM_NAME = "async_iter_async_fun"; - const FINISH_TEST = "finish_async_iterator_async_fun"; - const MARKER_EVENT = "async_iter_async_fun_marker"; - const doSomething = jest.fn(); - const doSomethingElse = jest.fn(); - - const markerEvent = jsonEvent({ - type: MARKER_EVENT, - data: { - message: "mark", - }, - }); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - const appendResult = await client.appendToStream( - STREAM_NAME_B, - markerEvent - ); - - const subscription = client.subscribeToAll({ - fromPosition: appendResult.position, - }); - - client.appendToStream(STREAM_NAME, [...jsonTestEvents(99), finishEvent]); - - const readEvents = new Set(); - - for await (const event of subscription) { - doSomething(event); - - if (!event.event?.type.startsWith("$")) { - doSomethingElse(event); - } - - if (event.event?.type === "test") { - // example of awaiting an async function when iterating over the async iterator - await delay(10); - - if (event.event.isJson) { - readEvents.add( - (event.event.data as unknown as TestEventData).index - ); - } - } - - if (event.event?.type === FINISH_TEST) { - break; - } - } - - expect(doSomething).toBeCalled(); - // unique numbers from 0 -> 98 - expect(readEvents.size).toBe(99); - expect(doSomethingElse).toBeCalledTimes(100); - }); - - test("after the fact event listeners", async () => { - const STREAM_NAME = "after_the_fact"; - const FINISH_TEST = "finish_after_the_fact"; - const MARKER_EVENT = "after_the_fact_marker"; - - const markerEvent = jsonEvent({ - type: MARKER_EVENT, - data: { - message: "mark", - }, - }); - - const finishEvent = jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }); - - const defer = new Defer(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(8)); - - const appendResult = await client.appendToStream( - STREAM_NAME, - markerEvent - ); - - const eventListenerOne = jest.fn(); - const eventListenerTwo = jest.fn(); - const endListener = jest.fn(defer.resolve); - const onceListener = jest.fn(); - const offListener = jest.fn(); - const caughtUpListener = jest.fn(); - - const subscription = client - .subscribeToAll({ - fromPosition: appendResult.position, - }) - .on("data", eventListenerOne) - .on("data", async (event) => { - eventListenerTwo(event); - - if (event.event?.type === FINISH_TEST) { - await delay(500); - subscription.unsubscribe(); - } - }) - .on("data", offListener) - .once("data", onceListener) - .on("end", endListener) - .on("caughtUp", caughtUpListener) - .off("data", offListener); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(5), - finishEvent, - ]); - - await defer.promise; - - expect(eventListenerOne).toBeCalledTimes(6); - expect(eventListenerTwo).toBeCalledTimes(6); - expect(onceListener).toBeCalledTimes(1); - expect(endListener).toBeCalledTimes(1); - expect(offListener).not.toBeCalled(); - if (matchServerVersion`>=23.10`) { - expect(caughtUpListener).toBeCalledTimes(1); - } - }); - - test("pipeline", async () => { - const STREAM_NAME = "pipeline test"; - const FINISH_TEST = "finish_pipeline"; - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(8), - jsonEvent({ - type: FINISH_TEST, - data: { - message: "lets wrap this up", - }, - }), - ]); - - const subscription = client.subscribeToStream(STREAM_NAME); - - const writeStream = new (class extends Writable { - public ids: string[] = []; - _write({ event }: ResolvedEvent, _encoding: string, done: () => void) { - this.ids.push(event!.id); - if (event?.type === FINISH_TEST) { - subscription.unsubscribe().then(done); - } else { - done(); - } - } - })({ objectMode: true }); - - await asyncPipeline(subscription as Readable, writeStream); - - expect(writeStream.ids).toHaveLength(9); - }); - }); -}); diff --git a/packages/test/src/streams/subscribeToStream.test.ts b/packages/test/src/streams/subscribeToStream.test.ts deleted file mode 100644 index 712d0d14..00000000 --- a/packages/test/src/streams/subscribeToStream.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -/** @jest-environment ./src/utils/enableVersionCheck.ts */ - -import { pipeline, Writable, Readable } from "stream"; -import { promisify } from "util"; -import { - createTestNode, - Defer, - delay, - jsonTestEvents, - matchServerVersion, -} from "@test-utils"; - -import { - EventStoreDBClient, - ResolvedEvent, - jsonEvent, - END, -} from "@eventstore/db-client"; - -const asyncPipeline = promisify(pipeline); - -describe("subscribeToStream", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - const finishEvent = () => - jsonEvent({ - type: "finish-test", - data: { - message: "lets wrap this up", - }, - }); - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - await client.appendToStream("out_of_stream_name", jsonTestEvents(4)); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("should subscribe to stream", () => { - test("from start", async () => { - const defer = new Defer(); - const STREAM_NAME = "from_start_test_stream_name"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleClose = jest.fn(); - const handleConfirmation = jest.fn(); - const handleEnd = jest.fn(defer.resolve); - const handleEvent = jest.fn((event: ResolvedEvent) => { - if (event.event?.type === "finish-test") { - subscription.unsubscribe(); - } - }); - const handleCaughtUp = jest.fn(() => { - try { - expect(handleEvent).toBeCalledTimes(4); - } catch (error) { - defer.reject(error); - } - }); - - const subscription = client - .subscribeToStream(STREAM_NAME) - .on("error", handleError) - .on("data", handleEvent) - .on("close", handleClose) - .on("confirmation", handleConfirmation) - .on("caughtUp", handleCaughtUp) - .on("end", handleEnd); - - await delay(500); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3), - finishEvent(), - ]); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handleConfirmation).toBeCalledTimes(1); - expect(handleEvent).toBeCalledTimes(8); - - if (matchServerVersion`>=23.10`) { - expect(handleCaughtUp).toBeCalledTimes(1); - } - }); - - test("from end", async () => { - const STREAM_NAME = "from_end_test_stream_name"; - const defer = new Defer(); - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleClose = jest.fn(); - const handleConfirmation = jest.fn(); - const handleEnd = jest.fn(defer.resolve); - const handleEvent = jest.fn((event: ResolvedEvent) => { - if (event.event?.type === "finish-test") { - subscription.unsubscribe(); - } - }); - const handleCaughtUp = jest.fn(() => { - try { - expect(handleEvent).toBeCalledTimes(0); - } catch (error) { - defer.reject(error); - } - }); - - const subscription = client - .subscribeToStream(STREAM_NAME, { - fromRevision: END, - }) - .on("error", handleError) - .on("data", handleEvent) - .on("close", handleClose) - .on("confirmation", handleConfirmation) - .on("caughtUp", handleCaughtUp) - .on("end", handleEnd); - - await delay(500); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3), - finishEvent(), - ]); - - await defer.promise; - - expect(handleError).not.toBeCalled(); - expect(handleConfirmation).toBeCalledTimes(1); - expect(handleEvent).toBeCalledTimes(4); - }); - - test("from revision", async () => { - const STREAM_NAME = "from_revision_test_stream_name"; - - await client.appendToStream(STREAM_NAME, jsonTestEvents(4)); - - const defer = new Defer(); - - const handleError = jest.fn((error) => { - defer.reject(error); - }); - const handleClose = jest.fn(); - const handleConfirmation = jest.fn(); - const handleEnd = jest.fn(defer.resolve); - const handleEvent = jest.fn((event: ResolvedEvent) => { - if (event.event?.type === "finish-test") { - subscription.unsubscribe(); - } - }); - const handleCaughtUp = jest.fn(() => { - try { - // It should throw 1 because we are starting from revision 2 - expect(handleEvent).toBeCalledTimes(1); - } catch (error) { - defer.reject(error); - } - }); - - const subscription = client - .subscribeToStream(STREAM_NAME, { - fromRevision: BigInt(2), - }) - .on("close", handleClose) - .on("error", handleError) - .on("data", handleEvent) - .on("close", handleClose) - .on("confirmation", handleConfirmation) - .on("caughtUp", handleCaughtUp) - .on("end", handleEnd); - - await delay(500); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(3), - finishEvent(), - ]); - - await defer.promise; - - expect(handleConfirmation).toBeCalledTimes(1); - expect(handleEvent).toBeCalledTimes(5); - expect(handleEnd).toBeCalledTimes(1); - expect(handleError).not.toBeCalled(); - - if (matchServerVersion`>=23.10`) { - expect(handleCaughtUp).toBeCalledTimes(1); - } - }); - }); - - describe("should return a readableStream", () => { - test("async iterator", async () => { - const STREAM_NAME = "async_iter"; - const doSomething = jest.fn(); - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(8), - finishEvent(), - ]); - - for await (const event of client.subscribeToStream(STREAM_NAME)) { - doSomething(event); - - if (event.event?.type === "finish-test") { - break; - } - } - - expect(doSomething).toBeCalledTimes(9); - }); - - test("pipeline", async () => { - const STREAM_NAME = "pipeline test"; - - await client.appendToStream(STREAM_NAME, [ - ...jsonTestEvents(8), - finishEvent(), - ]); - - const subscription = client.subscribeToStream(STREAM_NAME); - - const writeStream = new (class extends Writable { - public ids: string[] = []; - _write({ event }: ResolvedEvent, _encoding: string, done: () => void) { - this.ids.push(event!.id); - if (event?.type === "finish-test") { - subscription.unsubscribe().then(done); - } else { - done(); - } - } - })({ objectMode: true }); - - await asyncPipeline(subscription as Readable, writeStream); - - expect(writeStream.ids).toHaveLength(9); - }); - }); -}); diff --git a/packages/test/src/streams/systemStreams.test.ts b/packages/test/src/streams/systemStreams.test.ts deleted file mode 100644 index 43a0ca87..00000000 --- a/packages/test/src/streams/systemStreams.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - isMetastream, - isSystemStream, - metastreamOf, - originalStreamOf, -} from "@eventstore/db-client"; - -describe("systemStreams helpers", () => { - describe("isSystemStream", () => { - test.each([ - ["hello", false], - ["", false], - ["$hello", true], - ["$$hello", true], - ])("%s", (name, expected) => { - expect(isSystemStream(name)).toBe(expected); - }); - }); - - describe("isMetastream", () => { - test.each([ - ["hello", false], - ["", false], - ["$hello", false], - ["$$hello", true], - ])("%s", (name, expected) => { - expect(isMetastream(name)).toBe(expected); - }); - }); - - test("metastreamOf", () => { - expect(metastreamOf("hello")).toBe("$$hello"); - }); - - test("originalStreamOf", () => { - expect(originalStreamOf("$$hello")).toBe("hello"); - }); -}); diff --git a/packages/test/src/streams/tombstoneStream.test.ts b/packages/test/src/streams/tombstoneStream.test.ts deleted file mode 100644 index 655dbb91..00000000 --- a/packages/test/src/streams/tombstoneStream.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { collect, createTestNode, jsonTestEvents } from "@test-utils"; - -import { - WrongExpectedVersionError, - StreamDeletedError, - NO_STREAM, - EventStoreDBClient, - BACKWARDS, - END, -} from "@eventstore/db-client"; - -describe("tombstoneStream", () => { - describe("should successfully tombstone a stream", () => { - const node = createTestNode(); - let client!: EventStoreDBClient; - - beforeAll(async () => { - await node.up(); - client = new EventStoreDBClient( - { endpoint: node.uri }, - { rootCertificate: node.certs.root }, - { username: "admin", password: "changeit" } - ); - }); - - afterAll(async () => { - await node.down(); - }); - - describe("any revision", () => { - const ANY_REVISION_STREAM = "any_revision_stream"; - - beforeAll(async () => { - await client.appendToStream(ANY_REVISION_STREAM, jsonTestEvents()); - }); - - it("succeeds", async () => { - const result = await client.tombstoneStream(ANY_REVISION_STREAM); - expect(result).toBeDefined(); - - try { - for await (const event of client.readStream(ANY_REVISION_STREAM, { - maxCount: 10, - })) { - expect(event).toBe("Unreachable"); - } - } catch (error) { - expect(error).toBeInstanceOf(StreamDeletedError); - - if (error instanceof StreamDeletedError) { - expect(error.streamName).toBe(ANY_REVISION_STREAM); - } - } - }); - }); - - describe("expected revision", () => { - describe("exact", () => { - const STREAM = "expected_revision_stream_exact"; - - beforeAll(async () => { - await client.appendToStream(STREAM, jsonTestEvents()); - }); - - it("fails", async () => { - try { - const result = await client.tombstoneStream(STREAM, { - expectedRevision: BigInt(2), - }); - - expect(result).toBe("Unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM); - expect(error.expectedVersion).toBe(BigInt(2)); - } - } - }); - - it("succeeds", async () => { - let revision!: bigint; - - for await (const { event } of client.readStream(STREAM, { - maxCount: 1, - direction: BACKWARDS, - fromRevision: END, - })) { - revision = event!.revision; - } - - const result = await client.tombstoneStream(STREAM, { - expectedRevision: revision, - }); - - expect(result).toBeDefined(); - - try { - for await (const event of client.readStream(STREAM, { - maxCount: 10, - })) { - expect(event).toBeDefined(); - } - - expect(true).toBe("Unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(StreamDeletedError); - - if (error instanceof StreamDeletedError) { - expect(error.streamName).toBe(STREAM); - } - } - }); - }); - - describe("no stream", () => { - const NOT_A_STREAM = "expected_revision_stream_no_stream"; - const STREAM = "i_exist_hopefully"; - - beforeAll(async () => { - await client.appendToStream(STREAM, jsonTestEvents()); - }); - - it("fails", async () => { - try { - const result = await client.tombstoneStream(STREAM, { - expectedRevision: NO_STREAM, - }); - - expect(result).toBe("Unreachable"); - } catch (error) { - expect(error).toBeInstanceOf(WrongExpectedVersionError); - if (error instanceof WrongExpectedVersionError) { - expect(error.streamName).toBe(STREAM); - } - } - }); - - it("succeeds", async () => { - const result = await client.tombstoneStream(NOT_A_STREAM, { - expectedRevision: NO_STREAM, - }); - - expect(result).toBeDefined(); - - await expect(() => - collect(client.readStream(NOT_A_STREAM, { maxCount: 10 })) - ).rejects.toThrowError(StreamDeletedError); - }); - }); - }); - }); -}); diff --git a/packages/test/src/utils/collect.ts b/packages/test/src/utils/collect.ts index 1f267171..c05118c0 100644 --- a/packages/test/src/utils/collect.ts +++ b/packages/test/src/utils/collect.ts @@ -1,6 +1,6 @@ import type { StreamingRead } from "@eventstore/db-client"; -export const collect = async < +export const collectSimple = async < T extends StreamingRead, E = T extends StreamingRead ? E : unknown >( @@ -14,3 +14,19 @@ export const collect = async < return collected as E[]; }; + + +export const collect = async < + T extends AsyncIterableIterator, + E = T extends AsyncIterableIterator ? E : unknown +>( + stream: T +): Promise => { + const collected = []; + + for await (const e of stream) { + collected.push(e); + } + + return collected as E[]; +}; \ No newline at end of file