Wyle.Gong-巩文昕 7a1aae1e2f ui
2025-04-23 11:21:08 +08:00

687 lines
17 KiB
JavaScript

/*
* Copyright 2018-2020 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const test = require("ava");
const { connect, ErrorCode, createInbox, StringCodec, Empty } = require(
"./index",
);
const { deferred, delay } = require(
"../lib/nats-base-client/internal_mod",
);
const { Lock } = require("./helpers/lock");
const { NatsServer, wsConfig } = require("./helpers/launcher");
const { parseOptions } = require("../lib/nats-base-client/options");
test("basics - connect", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("basics - wss connection", async (t) => {
const servers = "wss://demo.nats.io:8443";
try {
const nc = await connect({ servers });
await nc.flush();
await nc.close();
t.is(nc.protocol.transport?.isEncrypted(), true);
t.pass();
} catch (err) {
t.fail(err.message);
}
});
test("basics - publish", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
nc.publish(createInbox());
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("basics - no publish without subject", async (t) => {
t.plan(1);
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
try {
nc.publish("");
fail("should not be able to publish without a subject");
} catch (err) {
t.is(err.code, ErrorCode.BadSubject);
} finally {
await nc.close();
await ns.stop();
}
});
test("basics - pubsub", async (t) => {
t.plan(1);
const ns = await NatsServer.start(wsConfig());
const subj = createInbox();
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const sub = nc.subscribe(subj);
const iter = (async () => {
for await (const m of sub) {
break;
}
})();
nc.publish(subj);
await iter;
t.is(sub.getProcessed(), 1);
await nc.close();
await ns.stop();
});
test("basics - subscribe and unsubscribe", async (t) => {
t.plan(12);
const ns = await NatsServer.start(wsConfig());
const subj = createInbox();
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const sub = nc.subscribe(subj, { max: 1000, queue: "aaa" });
// check the subscription
t.is(nc.protocol.subscriptions.size(), 1);
let s = nc.protocol.subscriptions.get(1);
t.truthy(s);
t.is(s.getReceived(), 0);
t.is(s.subject, subj);
t.truthy(s.callback);
t.is(s.max, 1000);
t.is(s.queue, "aaa");
// modify the subscription
sub.unsubscribe(10);
s = nc.protocol.subscriptions.get(1);
t.truthy(s);
t.is(s.max, 10);
// verify subscription updates on message
nc.publish(subj);
await nc.flush();
s = nc.protocol.subscriptions.get(1);
t.truthy(s);
t.is(s.getReceived(), 1);
// verify cleanup
sub.unsubscribe();
t.is(nc.protocol.subscriptions.size(), 0);
await nc.close();
await ns.stop();
});
test("basics - subscriptions iterate", async (t) => {
const ns = await NatsServer.start(wsConfig());
const lock = Lock();
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
const sub = nc.subscribe(subj);
const _ = (async () => {
for await (const m of sub) {
lock.unlock();
}
})();
nc.publish(subj);
await nc.flush();
await lock;
await nc.close();
await ns.stop();
t.pass();
});
test("basics - subscriptions pass exact subject to cb", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const s = createInbox();
const subj = `${s}.foo.bar.baz`;
const sub = nc.subscribe(`${s}.*.*.*`);
const sp = deferred();
const _ = (async () => {
for await (const m of sub) {
sp.resolve(m.subject);
break;
}
})();
nc.publish(subj);
await nc.flush();
t.is(await sp, subj);
await nc.close();
await ns.stop();
});
test("basics - subscribe returns Subscription", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
const sub = nc.subscribe(subj);
t.is(sub.sid, 1);
t.is(typeof sub.getReceived, "function");
t.is(typeof sub.drain, "function");
t.is(typeof sub.isClosed, "function");
t.is(typeof sub.callback, "function");
t.is(typeof sub.getSubject, "function");
t.is(typeof sub.getID, "function");
t.is(typeof sub.getMax, "function");
await nc.close();
await ns.stop();
});
test("basics - wildcard subscriptions", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const single = 3;
const partial = 2;
const full = 5;
const s = createInbox();
const sub = nc.subscribe(`${s}.*`);
const sub2 = nc.subscribe(`${s}.foo.bar.*`);
const sub3 = nc.subscribe(`${s}.foo.>`);
nc.publish(`${s}.bar`);
nc.publish(`${s}.baz`);
nc.publish(`${s}.foo.bar.1`);
nc.publish(`${s}.foo.bar.2`);
nc.publish(`${s}.foo.baz.3`);
nc.publish(`${s}.foo.baz.foo`);
nc.publish(`${s}.foo.baz`);
nc.publish(`${s}.foo`);
await nc.drain();
t.is(sub.getReceived(), single, "single");
t.is(sub2.getReceived(), partial, "partial");
t.is(sub3.getReceived(), full, "full");
await ns.stop();
});
test("basics - correct data in message", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const sc = StringCodec();
const subj = createInbox();
const mp = deferred();
const sub = nc.subscribe(subj);
const _ = (async () => {
for await (const m of sub) {
mp.resolve(m);
break;
}
})();
nc.publish(subj, sc.encode(subj));
const m = await mp;
t.is(m.subject, subj);
t.is(sc.decode(m.data), subj);
t.is(m.reply, "");
await nc.close();
await ns.stop();
});
test("basics - correct reply in message", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const s = createInbox();
const r = createInbox();
const rp = deferred();
const sub = nc.subscribe(s);
const _ = (async () => {
for await (const m of sub) {
rp.resolve(m.reply);
break;
}
})();
nc.publish(s, Empty, { reply: r });
t.is(await rp, r);
await nc.close();
await ns.stop();
});
test("basics - respond returns false if no reply subject set", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
let s = createInbox();
const dr = deferred();
const sub = nc.subscribe(s);
const _ = (async () => {
for await (const m of sub) {
dr.resolve(m.respond());
break;
}
})();
nc.publish(s);
const responded = await dr;
t.false(responded);
await nc.close();
await ns.stop();
});
test("basics - closed cannot subscribe", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
await nc.close();
await ns.stop();
let failed = false;
try {
nc.subscribe(createInbox());
t.fail("should have not been able to subscribe");
} catch (err) {
failed = true;
}
t.true(failed);
});
test("basics - close cannot request", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
await nc.close();
await ns.stop();
let failed = false;
try {
await nc.request(createInbox());
t.fail("should have not been able to request");
} catch (err) {
failed = true;
}
t.true(failed);
});
test("basics - flush returns promise", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
let p = nc.flush();
if (!p) {
t.fail("should have returned a promise");
}
await p;
t.pass();
await nc.close();
await ns.stop();
});
test("basics - unsubscribe after close", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
let sub = nc.subscribe(createInbox());
await nc.close();
sub.unsubscribe();
await ns.stop();
t.pass();
});
test("basics - unsubscribe stops messages", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
// in this case we use a callback otherwise messages are buffered.
const sub = nc.subscribe(subj, {
callback: () => {
sub.unsubscribe();
},
});
nc.publish(subj);
nc.publish(subj);
nc.publish(subj);
nc.publish(subj);
await nc.flush();
t.is(sub.getReceived(), 1);
await nc.close();
await ns.stop();
});
test("basics - request", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const sc = StringCodec();
const s = createInbox();
const sub = nc.subscribe(s);
const _ = (async () => {
for await (const m of sub) {
m.respond(sc.encode("foo"));
}
})();
const msg = await nc.request(s);
await nc.close();
t.is(sc.decode(msg.data), "foo");
await ns.stop();
});
test("basics - request timeout", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const s = createInbox();
const lock = Lock();
nc.request(s, Empty, { timeout: 100 })
.then(() => {
fail();
})
.catch((err) => {
t.true(
err.code === ErrorCode.Timeout || err.code === ErrorCode.NoResponders,
);
lock.unlock();
});
await lock;
await nc.close();
await ns.stop();
});
test("basics - request cancel rejects", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const s = createInbox();
const lock = Lock();
nc.request(s, Empty, { timeout: 1000 })
.then(() => {
t.fail();
})
.catch((err) => {
t.is(err.code, ErrorCode.Cancelled);
lock.unlock();
});
nc.protocol.muxSubscriptions.reqs.forEach((v) => {
v.cancel();
});
await lock;
await nc.close();
await ns.stop();
});
test("basics - subscription with timeout", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const lock = Lock(1);
const sub = nc.subscribe(createInbox(), { max: 1, timeout: 250 });
(async () => {
for await (const m of sub) {}
})().catch((err) => {
t.is(err.code, ErrorCode.Timeout);
lock.unlock();
});
await lock;
await nc.close();
await ns.stop();
});
test("basics - subscription expecting 2 doesn't fire timeout", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
const sub = nc.subscribe(subj, { max: 2, timeout: 500 });
(async () => {
for await (const m of sub) {}
})().catch((err) => {
t.fail(err);
});
nc.publish(subj);
await nc.flush();
await delay(1000);
t.is(sub.getReceived(), 1);
await nc.close();
await ns.stop();
});
test("basics - subscription timeout auto cancels", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
let c = 0;
const sub = nc.subscribe(subj, { max: 2, timeout: 300 });
(async () => {
for await (const m of sub) {
c++;
}
})().catch((err) => {
t.fail(err);
});
nc.publish(subj);
nc.publish(subj);
await delay(500);
t.is(c, 2);
await nc.close();
await ns.stop();
});
test("basics - no mux requests create normal subs", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const _ = nc.request(createInbox(), Empty, { timeout: 1000, noMux: true });
t.is(nc.protocol.subscriptions.size(), 1);
t.is(nc.protocol.muxSubscriptions.size(), 0);
const sub = nc.protocol.subscriptions.get(1);
t.truthy(sub);
t.is(sub.max, 1);
sub.unsubscribe();
t.is(nc.protocol.subscriptions.size(), 0);
await nc.close();
await ns.stop();
});
test("basics - no mux requests timeout", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const lock = Lock();
nc.request(createInbox(), Empty, { timeout: 250, noMux: true })
.catch((err) => {
t.true(
err.code === ErrorCode.Timeout || err.code === ErrorCode.NoResponders,
);
lock.unlock();
});
await lock;
await nc.close();
await ns.stop();
});
test("basics - no mux requests", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
const sub = nc.subscribe(subj);
const data = Uint8Array.from([1234]);
(async () => {
for await (const m of sub) {
m.respond(data);
}
})().then();
const m = await nc.request(subj, Empty, { timeout: 1000, noMux: true });
t.deepEqual(Uint8Array.from(m.data), data);
await nc.close();
await ns.stop();
});
test("basics - no max_payload messages", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
t.truthy(nc.protocol.info.max_payload);
const big = new Uint8Array(nc.protocol.info.max_payload + 1);
const subj = createInbox();
try {
nc.publish(subj, big);
t.fail();
} catch (err) {
t.is(err.code, ErrorCode.MaxPayloadExceeded);
}
try {
const _ = await nc.request(subj, big);
t.fail();
} catch (err) {
t.is(err.code, ErrorCode.MaxPayloadExceeded);
}
const sub = nc.subscribe(subj);
(async () => {
for await (const m of sub) {
m.respond(big);
t.fail();
}
})().catch((err) => {
t.is(err.code, ErrorCode.MaxPayloadExceeded);
});
await nc.request(subj).then(() => {
t.fail();
}).catch((err) => {
t.is(err.code, ErrorCode.Timeout);
});
await nc.close();
await ns.stop();
});
test("basics - empty message", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
const mp = deferred();
const sub = nc.subscribe(subj);
const _ = (async () => {
for await (const m of sub) {
mp.resolve(m);
break;
}
})();
nc.publish(subj);
const m = await mp;
t.is(m.subject, subj);
t.is(m.data.length, 0);
await nc.close();
await ns.stop();
});
test("basics - subject is required", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
t.plan(2);
t.throws(() => {
nc.publish();
}, { code: ErrorCode.BadSubject });
await nc.request().catch((err) => {
t.is(err.code, ErrorCode.BadSubject);
});
await nc.close();
await ns.stop();
});
test("basics - disconnect reconnects", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const lock = new Lock();
const status = nc.status();
(async () => {
for await (const s of status) {
switch (s.type) {
case "reconnect":
lock.unlock();
break;
default:
}
}
})().then();
nc.protocol.transport.disconnect();
await lock;
await nc.close();
await ns.stop();
t.pass();
});
test("basics - wsnats doesn't support tls options", async (t) => {
try {
await connect({ servers: "wss://demo.nats.io:8443", tls: {} });
t.fail(`should have failed with ${ErrorCode.InvalidOption}`);
} catch (err) {
t.is(err.code, ErrorCode.InvalidOption);
}
t.pass();
});
test("basics - drain connection publisher", async (t) => {
const ns = await NatsServer.start(wsConfig());
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const nc2 = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
const subj = createInbox();
const lock = new Lock(5);
nc2.subscribe(subj, {
callback: (err, m) => {
lock.unlock();
},
});
await nc2.flush();
for (let i = 0; i < 5; i++) {
nc.publish(subj);
}
await nc.drain();
await lock;
await nc.close();
t.pass();
});
test("basics - default connection", async (t) => {
const opts = parseOptions({});
t.is(opts.servers.length, 1);
t.is(opts.servers[0], `127.0.0.1:443`);
});