serverless-ircd 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +40 -3
- package/.github/workflows/deploy-aws.yml +1 -2
- package/.github/workflows/deploy-cf-tcp.yml +1 -5
- package/.github/workflows/deploy-cf.yml +1 -2
- package/.gitmodules +3 -0
- package/CHANGELOG.md +474 -0
- package/README.md +219 -22
- package/apps/aws-stack/README.md +33 -0
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +74 -9
- package/apps/aws-stack/tests/coverage-command.test.ts +155 -0
- package/apps/aws-stack/tests/stack.test.ts +2 -2
- package/apps/aws-stack/tests/synth-no-bundle.test.ts +129 -0
- package/apps/aws-stack/tests/vitest.setup.ts +18 -0
- package/apps/aws-stack/vitest.config.ts +3 -0
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-tcp-container/src/config-loader.ts +6 -7
- package/apps/cf-tcp-container/src/container-server.ts +3 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +10 -3
- package/apps/cf-tcp-container/wrangler.toml +1 -1
- package/apps/cf-worker/package.json +2 -1
- package/apps/cf-worker/src/origin-allowlist.ts +99 -0
- package/apps/cf-worker/src/worker.ts +89 -9
- package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +18 -0
- package/apps/cf-worker/tests/fixtures/web-dist/index.html +18 -0
- package/apps/cf-worker/tests/origin-allowlist.test.ts +101 -0
- package/apps/cf-worker/tests/smoke.test.ts +28 -2
- package/apps/cf-worker/tests/ws-origin.test.ts +89 -0
- package/apps/cf-worker/vitest.config.ts +1 -1
- package/apps/cf-worker/wrangler.test.toml +23 -0
- package/apps/cf-worker/wrangler.toml +59 -3
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +6 -6
- package/apps/local-cli/src/main.ts +20 -0
- package/apps/local-cli/src/server.ts +9 -7
- package/apps/local-cli/tests/e2e.test.ts +112 -28
- package/apps/local-cli/tests/tcp.test.ts +71 -19
- package/apps/local-cli/tests/ws-subprotocol.test.ts +12 -7
- package/apps/web/landing/index.html +217 -0
- package/apps/web/package.json +28 -0
- package/apps/web/scripts/build.mjs +125 -0
- package/apps/web/src/build-env.ts +43 -0
- package/apps/web/src/config-schema.ts +138 -0
- package/apps/web/static/config.json +28 -0
- package/apps/web/static/config.prod.json +28 -0
- package/apps/web/static/config.staging.json +28 -0
- package/apps/web/tests/build-env.test.ts +63 -0
- package/apps/web/tests/build-smoke.test.ts +103 -0
- package/apps/web/tests/config-schema.test.ts +432 -0
- package/apps/web/tests/workspace.test.ts +12 -0
- package/apps/web/tsconfig.json +10 -0
- package/apps/web/upstream/.browserslistrc +3 -0
- package/apps/web/upstream/.editorconfig +9 -0
- package/apps/web/upstream/.eslintignore +3 -0
- package/apps/web/upstream/.eslintrc.js +90 -0
- package/apps/web/upstream/.github/FUNDING.yml +12 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/apps/web/upstream/.github/workflows/push.yml +26 -0
- package/apps/web/upstream/.github/workflows/release.yml +33 -0
- package/apps/web/upstream/.github/workflows/release_manual.yml +55 -0
- package/apps/web/upstream/.prettierrc.js +9 -0
- package/apps/web/upstream/.stylelintignore +5 -0
- package/apps/web/upstream/.stylelintrc.js +21 -0
- package/apps/web/upstream/LICENSE +201 -0
- package/apps/web/upstream/README.md +76 -0
- package/apps/web/upstream/babel.config.js +25 -0
- package/apps/web/upstream/crowdin.yml +3 -0
- package/apps/web/upstream/index.html +35 -0
- package/apps/web/upstream/jest.config.js +7 -0
- package/apps/web/upstream/jsdoc.json +22 -0
- package/apps/web/upstream/package.json +80 -0
- package/apps/web/upstream/scripts/pre-commit +43 -0
- package/apps/web/upstream/src/components/App.vue +516 -0
- package/apps/web/upstream/src/components/AppSettings.vue +587 -0
- package/apps/web/upstream/src/components/AutoComplete.vue +284 -0
- package/apps/web/upstream/src/components/Avatar.vue +105 -0
- package/apps/web/upstream/src/components/AwayStatusIndicator.vue +77 -0
- package/apps/web/upstream/src/components/BufferKey.vue +121 -0
- package/apps/web/upstream/src/components/BufferSettings.vue +97 -0
- package/apps/web/upstream/src/components/Captcha.vue +85 -0
- package/apps/web/upstream/src/components/ChannelBanlist.vue +96 -0
- package/apps/web/upstream/src/components/ChannelInfo.vue +159 -0
- package/apps/web/upstream/src/components/ChannelInvitelist.vue +276 -0
- package/apps/web/upstream/src/components/ChannelList.vue +345 -0
- package/apps/web/upstream/src/components/Container.vue +401 -0
- package/apps/web/upstream/src/components/ContainerHeader.vue +522 -0
- package/apps/web/upstream/src/components/ControlInput.vue +1016 -0
- package/apps/web/upstream/src/components/LoadingAnimation.vue +104 -0
- package/apps/web/upstream/src/components/MediaViewer.vue +199 -0
- package/apps/web/upstream/src/components/MessageInfo.vue +192 -0
- package/apps/web/upstream/src/components/MessageList.vue +1028 -0
- package/apps/web/upstream/src/components/MessageListAvatar.vue +20 -0
- package/apps/web/upstream/src/components/MessageListMessageCompact.vue +361 -0
- package/apps/web/upstream/src/components/MessageListMessageInline.vue +257 -0
- package/apps/web/upstream/src/components/MessageListMessageModern.vue +430 -0
- package/apps/web/upstream/src/components/NetworkSettings.vue +528 -0
- package/apps/web/upstream/src/components/Nicklist.vue +309 -0
- package/apps/web/upstream/src/components/NicklistUser.vue +181 -0
- package/apps/web/upstream/src/components/NotConnected.vue +271 -0
- package/apps/web/upstream/src/components/SelfUser.vue +283 -0
- package/apps/web/upstream/src/components/ServerSelector.vue +162 -0
- package/apps/web/upstream/src/components/ServerView.vue +126 -0
- package/apps/web/upstream/src/components/SettingsAdvanced.vue +232 -0
- package/apps/web/upstream/src/components/SettingsAliases.vue +121 -0
- package/apps/web/upstream/src/components/Sidebar.vue +429 -0
- package/apps/web/upstream/src/components/SidebarAboutBuffer.vue +288 -0
- package/apps/web/upstream/src/components/SidebarState.vue +145 -0
- package/apps/web/upstream/src/components/StartupError.vue +35 -0
- package/apps/web/upstream/src/components/StateBrowser.vue +420 -0
- package/apps/web/upstream/src/components/StateBrowserBuffer.vue +124 -0
- package/apps/web/upstream/src/components/StateBrowserNetwork.vue +823 -0
- package/apps/web/upstream/src/components/StateBrowserUsermenu.vue +162 -0
- package/apps/web/upstream/src/components/TypingStatusIndicator.vue +34 -0
- package/apps/web/upstream/src/components/TypingUsersList.vue +56 -0
- package/apps/web/upstream/src/components/UrlEmbed.vue +149 -0
- package/apps/web/upstream/src/components/UserBox.vue +728 -0
- package/apps/web/upstream/src/components/inputtools/Emoji.vue +67 -0
- package/apps/web/upstream/src/components/inputtools/TextStyle.vue +142 -0
- package/apps/web/upstream/src/components/startups/CommonLayout.vue +208 -0
- package/apps/web/upstream/src/components/startups/CustomServer.vue +495 -0
- package/apps/web/upstream/src/components/startups/KiwiBnc.vue +361 -0
- package/apps/web/upstream/src/components/startups/Personal.vue +317 -0
- package/apps/web/upstream/src/components/startups/Welcome.vue +544 -0
- package/apps/web/upstream/src/components/startups/ZncLogin.vue +229 -0
- package/apps/web/upstream/src/components/utils/InputConfirm.vue +68 -0
- package/apps/web/upstream/src/components/utils/InputPrompt.vue +118 -0
- package/apps/web/upstream/src/components/utils/InputText.vue +172 -0
- package/apps/web/upstream/src/components/utils/IrcInput.vue +579 -0
- package/apps/web/upstream/src/components/utils/PluginWrapper.vue +26 -0
- package/apps/web/upstream/src/components/utils/TabbedView.vue +149 -0
- package/apps/web/upstream/src/components/utils/TransitionExpand.vue +97 -0
- package/apps/web/upstream/src/helpers/Colours.js +128 -0
- package/apps/web/upstream/src/helpers/IrcdDiffs.js +26 -0
- package/apps/web/upstream/src/helpers/Md5.js +193 -0
- package/apps/web/upstream/src/helpers/Misc.js +401 -0
- package/apps/web/upstream/src/helpers/TextFormatting.js +237 -0
- package/apps/web/upstream/src/libs/AliasRewriter.js +157 -0
- package/apps/web/upstream/src/libs/AudioManager.js +60 -0
- package/apps/web/upstream/src/libs/BouncerMiddleware.js +247 -0
- package/apps/web/upstream/src/libs/BouncerProvider.js +551 -0
- package/apps/web/upstream/src/libs/ChathistoryMiddleware.js +153 -0
- package/apps/web/upstream/src/libs/ConfigLoader.js +94 -0
- package/apps/web/upstream/src/libs/EmojiProvider.js +46 -0
- package/apps/web/upstream/src/libs/GlobalApi.js +346 -0
- package/apps/web/upstream/src/libs/IPC.js +51 -0
- package/apps/web/upstream/src/libs/InputHandler.js +921 -0
- package/apps/web/upstream/src/libs/IrcClient.js +1506 -0
- package/apps/web/upstream/src/libs/Logger.js +71 -0
- package/apps/web/upstream/src/libs/Message.js +169 -0
- package/apps/web/upstream/src/libs/MessageFormatter.js +364 -0
- package/apps/web/upstream/src/libs/MessageParser.js +249 -0
- package/apps/web/upstream/src/libs/Notifications.js +78 -0
- package/apps/web/upstream/src/libs/ServerConnection.js +270 -0
- package/apps/web/upstream/src/libs/ServerSession.js +102 -0
- package/apps/web/upstream/src/libs/SoundBleep.js +20 -0
- package/apps/web/upstream/src/libs/StatePersistence.js +90 -0
- package/apps/web/upstream/src/libs/ThemeManager.js +128 -0
- package/apps/web/upstream/src/libs/TypingMiddleware.js +105 -0
- package/apps/web/upstream/src/libs/WindowTitle.js +63 -0
- package/apps/web/upstream/src/libs/batchedAdd.js +74 -0
- package/apps/web/upstream/src/libs/bufferTools.js +177 -0
- package/apps/web/upstream/src/libs/polyfill/Element.closest.js +18 -0
- package/apps/web/upstream/src/libs/renderers/Html.js +113 -0
- package/apps/web/upstream/src/libs/settingTools.js +31 -0
- package/apps/web/upstream/src/libs/state/BufferState.js +703 -0
- package/apps/web/upstream/src/libs/state/NetworkState.js +158 -0
- package/apps/web/upstream/src/libs/state/UserState.js +89 -0
- package/apps/web/upstream/src/libs/state/common.js +26 -0
- package/apps/web/upstream/src/libs/state.js +961 -0
- package/apps/web/upstream/src/libs/storage/Local.js +51 -0
- package/apps/web/upstream/src/main.js +566 -0
- package/apps/web/upstream/src/res/autocompleteCommands.js +31 -0
- package/apps/web/upstream/src/res/configTemplates.js +373 -0
- package/apps/web/upstream/src/res/globalStyle.css +277 -0
- package/apps/web/upstream/src/res/kiwiLoadingLogo.png +0 -0
- package/apps/web/upstream/src/res/locales/app.af-ZA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ar-SA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bg-BG.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bs-BA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ca-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.cs-CZ.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.da-DK.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.de-DE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.dev.po +1246 -0
- package/apps/web/upstream/src/res/locales/app.el-GR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.en-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-419.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-AR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-EM.po +536 -0
- package/apps/web/upstream/src/res/locales/app.es-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.eu-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fi-FI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fr-FR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.gl-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.he-IL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hi-IN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hu-HU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.id-ID.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.it-IT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ja-JP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ko-KR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.nl-NL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.no-NO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pl-PL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-BR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-PT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ro-RO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ru-RU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sl-SI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sq-AL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sr-SP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sv-SE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.tr-TR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.uk-UA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.vi-VN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-CN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-TW.po +1145 -0
- package/apps/web/upstream/src/res/localesList.json +27 -0
- package/apps/web/upstream/src/res/logo.png +0 -0
- package/apps/web/upstream/src/thirdparty/about.html +43 -0
- package/apps/web/upstream/src/thirdparty/index.js +5 -0
- package/apps/web/upstream/src/thirdparty/kiwiirccom.vue +185 -0
- package/apps/web/upstream/static/config.json +28 -0
- package/apps/web/upstream/static/emoticons/smile.png +0 -0
- package/apps/web/upstream/static/favicon.png +0 -0
- package/apps/web/upstream/static/highlight.mp3 +0 -0
- package/apps/web/upstream/static/highlight.ogg +0 -0
- package/apps/web/upstream/static/locales/.gitignore +2 -0
- package/apps/web/upstream/static/plugins/customise.html.example +10 -0
- package/apps/web/upstream/static/themes/coffee/theme.css +145 -0
- package/apps/web/upstream/static/themes/common/base.css +1064 -0
- package/apps/web/upstream/static/themes/dark/theme.css +196 -0
- package/apps/web/upstream/static/themes/default/theme.css +92 -0
- package/apps/web/upstream/static/themes/elite/theme.css +248 -0
- package/apps/web/upstream/static/themes/grayfox/theme.css +143 -0
- package/apps/web/upstream/static/themes/nightswatch/theme.css +367 -0
- package/apps/web/upstream/static/themes/osprey/theme.css +115 -0
- package/apps/web/upstream/static/themes/radioactive/theme.css +1211 -0
- package/apps/web/upstream/static/themes/sky/theme.css +107 -0
- package/apps/web/upstream/tests/unit/BatchAdd.spec.js +174 -0
- package/apps/web/upstream/tests/unit/MessageParser.spec.js +125 -0
- package/apps/web/upstream/tests/unit/Misc.spec.js +24 -0
- package/apps/web/upstream/tests/unit/NetworkState.spec.js +58 -0
- package/apps/web/upstream/tests/unit/StartupError.spec.js +20 -0
- package/apps/web/upstream/vue.config.js +150 -0
- package/apps/web/upstream/yarn.lock +9767 -0
- package/apps/web/vitest.config.ts +13 -0
- package/biome.json +2 -0
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +110 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +96 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/ADR-009-dual-transport-wss-and-irc-tls.md +138 -0
- package/docs/ADR-Index.md +15 -0
- package/docs/AWS-Adapter-Architecture.md +496 -0
- package/docs/AWS-Deployment.md +1275 -0
- package/docs/AWS-TCP-Deployment.md +410 -0
- package/docs/Cloudflare-Deployment-Guide.md +719 -0
- package/docs/Cloudflare-TCP-Deployment.md +510 -0
- package/docs/Home.md +16 -0
- package/docs/Observability.md +87 -0
- package/docs/PlanExtensions.md +812 -0
- package/docs/PlanIRCv3Websocket.md +503 -0
- package/docs/PlanWebClient.md +451 -0
- package/docs/Release-Process.md +443 -0
- package/docs/WebClientGuide.md +535 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/handlers/default.ts +3 -0
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +9 -0
- package/packages/aws-adapter/src/serialize.ts +29 -1
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +49 -8
- package/packages/aws-adapter/tests/account-store.test.ts +4 -7
- package/packages/aws-adapter/tests/aws-harness.ts +5 -2
- package/packages/aws-adapter/tests/aws-integration.test.ts +8 -4
- package/packages/aws-adapter/tests/aws-runtime.test.ts +79 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +11 -2
- package/packages/aws-adapter/tests/connect.test.ts +9 -9
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +3 -4
- package/packages/aws-adapter/tests/global-setup.ts +55 -30
- package/packages/aws-adapter/tests/gone-exception.test.ts +4 -9
- package/packages/aws-adapter/tests/handlers.test.ts +90 -4
- package/packages/aws-adapter/tests/message-store.test.ts +6 -5
- package/packages/aws-adapter/tests/nlb-stream.test.ts +60 -8
- package/packages/cf-adapter/package.json +2 -1
- package/packages/cf-adapter/src/connection-do.ts +3 -0
- package/packages/cf-adapter/src/serialize.ts +25 -4
- package/packages/cf-adapter/tests/cf-harness.ts +12 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +269 -0
- package/packages/cf-adapter/tests/channel-do.test.ts +117 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +49 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +91 -0
- package/packages/cf-adapter/tests/d1-account-store.test.ts +48 -0
- package/packages/cf-adapter/tests/registry-do.test.ts +104 -0
- package/packages/cf-adapter/tests/serialize.test.ts +308 -0
- package/packages/cf-adapter/vitest.config.ts +1 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/reports/mutation/mutation.html +342 -0
- package/packages/irc-core/src/caps/capabilities.ts +37 -3
- package/packages/irc-core/src/caps/index.ts +1 -0
- package/packages/irc-core/src/caps/sts.ts +84 -0
- package/packages/irc-core/src/commands/account-notify.ts +53 -0
- package/packages/irc-core/src/commands/away.ts +9 -3
- package/packages/irc-core/src/commands/cap.ts +23 -5
- package/packages/irc-core/src/commands/chathistory.ts +40 -11
- package/packages/irc-core/src/commands/index.ts +34 -0
- package/packages/irc-core/src/commands/isupport.ts +53 -0
- package/packages/irc-core/src/commands/mode.ts +8 -1
- package/packages/irc-core/src/commands/monitor.ts +327 -0
- package/packages/irc-core/src/commands/multiline.ts +256 -0
- package/packages/irc-core/src/commands/pre-away.ts +112 -0
- package/packages/irc-core/src/commands/privmsg.ts +19 -12
- package/packages/irc-core/src/commands/read-marker.ts +108 -0
- package/packages/irc-core/src/commands/registration.ts +8 -0
- package/packages/irc-core/src/commands/sasl.ts +24 -1
- package/packages/irc-core/src/commands/tagmsg.ts +71 -1
- package/packages/irc-core/src/commands/whois.ts +5 -0
- package/packages/irc-core/src/config.ts +65 -10
- package/packages/irc-core/src/effects.ts +14 -0
- package/packages/irc-core/src/ports.ts +389 -0
- package/packages/irc-core/src/protocol/index.ts +14 -0
- package/packages/irc-core/src/protocol/labeled-response.ts +133 -0
- package/packages/irc-core/src/protocol/numerics.ts +15 -0
- package/packages/irc-core/src/protocol/outbound.ts +16 -1
- package/packages/irc-core/src/protocol/standard-replies.ts +277 -0
- package/packages/irc-core/src/state/connection.ts +32 -1
- package/packages/irc-core/src/types.ts +112 -0
- package/packages/irc-core/stryker.commands.conf.json +1 -2
- package/packages/irc-core/tests/away-store.test.ts +73 -0
- package/packages/irc-core/tests/batch.test.ts +15 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +119 -2
- package/packages/irc-core/tests/caps/sts.test.ts +123 -0
- package/packages/irc-core/tests/commands/account-notify.test.ts +171 -0
- package/packages/irc-core/tests/commands/away.test.ts +85 -2
- package/packages/irc-core/tests/commands/cap.test.ts +165 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +148 -1
- package/packages/irc-core/tests/commands/echo-message.test.ts +9 -3
- package/packages/irc-core/tests/commands/isupport.test.ts +80 -0
- package/packages/irc-core/tests/commands/mode.test.ts +72 -0
- package/packages/irc-core/tests/commands/monitor.test.ts +411 -0
- package/packages/irc-core/tests/commands/multiline.test.ts +428 -0
- package/packages/irc-core/tests/commands/pre-away.test.ts +241 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +154 -3
- package/packages/irc-core/tests/commands/read-marker.test.ts +211 -0
- package/packages/irc-core/tests/commands/registration.test.ts +64 -0
- package/packages/irc-core/tests/commands/sasl.test.ts +116 -2
- package/packages/irc-core/tests/commands/tagmsg.test.ts +357 -0
- package/packages/irc-core/tests/commands/whois.test.ts +34 -0
- package/packages/irc-core/tests/config.test.ts +103 -13
- package/packages/irc-core/tests/labeled-response.test.ts +181 -0
- package/packages/irc-core/tests/message-tags.test.ts +114 -0
- package/packages/irc-core/tests/monitor-store.test.ts +200 -0
- package/packages/irc-core/tests/numerics.test.ts +13 -0
- package/packages/irc-core/tests/parser.test.ts +109 -0
- package/packages/irc-core/tests/read-marker-store.test.ts +108 -0
- package/packages/irc-core/tests/standard-replies.test.ts +412 -0
- package/packages/irc-core/tests/types.test.ts +27 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +450 -3
- package/packages/irc-server/src/dispatch.ts +108 -16
- package/packages/irc-server/tests/actor.test.ts +1432 -39
- package/packages/irc-server/tests/dispatch.test.ts +263 -2
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/index.ts +6 -0
- package/packages/irc-test-support/src/scenarios.ts +24 -2
- package/packages/irc-test-support/src/test-config.ts +54 -0
- package/packages/irc-test-support/tests/test-config.test.ts +51 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/ci-hardening/tests/ddb-local.test.ts +82 -0
- package/tools/load-test/package.json +33 -0
- package/tools/load-test/src/client.ts +351 -0
- package/tools/load-test/src/config.ts +313 -0
- package/tools/load-test/src/harness.ts +116 -0
- package/tools/load-test/src/main.ts +120 -0
- package/tools/load-test/src/metrics.ts +168 -0
- package/tools/load-test/src/report.ts +106 -0
- package/tools/load-test/tests/client.test.ts +212 -0
- package/tools/load-test/tests/config.test.ts +152 -0
- package/tools/load-test/tests/framing.test.ts +37 -0
- package/tools/load-test/tests/harness.test.ts +165 -0
- package/tools/load-test/tests/metrics.test.ts +174 -0
- package/tools/load-test/tests/report.test.ts +161 -0
- package/tools/load-test/tests/smoke.test.ts +67 -0
- package/tools/load-test/tsconfig.build.json +12 -0
- package/tools/load-test/tsconfig.test.json +10 -0
- package/tools/load-test/vitest.config.ts +29 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
|
@@ -294,7 +294,13 @@ function capAwareRuntime(members: ReadonlyMap<ConnId, MemberInfo>): {
|
|
|
294
294
|
registration: 'registered',
|
|
295
295
|
caps: info.caps,
|
|
296
296
|
joinedChannels: new Set<string>(),
|
|
297
|
-
userModes: {
|
|
297
|
+
userModes: {
|
|
298
|
+
invisible: false,
|
|
299
|
+
oper: false,
|
|
300
|
+
wallops: false,
|
|
301
|
+
serverNotices: false,
|
|
302
|
+
tls: false,
|
|
303
|
+
},
|
|
298
304
|
lastSeen: 0,
|
|
299
305
|
connectedSince: 0,
|
|
300
306
|
};
|
|
@@ -479,7 +485,13 @@ describe('dispatch — Broadcast cap / capLines', () => {
|
|
|
479
485
|
registration: 'registered',
|
|
480
486
|
caps: new Set(['chghost']),
|
|
481
487
|
joinedChannels: new Set<string>(),
|
|
482
|
-
userModes: {
|
|
488
|
+
userModes: {
|
|
489
|
+
invisible: false,
|
|
490
|
+
oper: false,
|
|
491
|
+
wallops: false,
|
|
492
|
+
serverNotices: false,
|
|
493
|
+
tls: false,
|
|
494
|
+
},
|
|
483
495
|
lastSeen: 0,
|
|
484
496
|
connectedSince: 0,
|
|
485
497
|
};
|
|
@@ -532,6 +544,255 @@ describe('dispatch — Broadcast cap / capLines', () => {
|
|
|
532
544
|
});
|
|
533
545
|
});
|
|
534
546
|
|
|
547
|
+
// ============================================================================
|
|
548
|
+
// dispatch — Broadcast `caps` (OR semantics)
|
|
549
|
+
//
|
|
550
|
+
// IRCv3 `draft/typing` ships a TAGMSG to peers that announced EITHER
|
|
551
|
+
// `draft/typing` OR `message-tags`. The reducer annotates the Broadcast
|
|
552
|
+
// effect with `caps` (plural); dispatch resolves per-recipient with OR
|
|
553
|
+
// semantics: a member receives `lines` iff they hold ANY of the listed caps.
|
|
554
|
+
// ============================================================================
|
|
555
|
+
|
|
556
|
+
describe('dispatch — Broadcast caps (OR semantics)', () => {
|
|
557
|
+
it('delivers lines to a member holding any of the listed caps', async () => {
|
|
558
|
+
const members = memberMap([
|
|
559
|
+
['c1', { caps: new Set(['message-tags']) }],
|
|
560
|
+
['c2', { caps: new Set(['draft/typing']) }],
|
|
561
|
+
['c3', { caps: new Set<string>() }],
|
|
562
|
+
]);
|
|
563
|
+
const { runtime, sends, broadcasts } = capAwareRuntime(members);
|
|
564
|
+
|
|
565
|
+
await dispatch(
|
|
566
|
+
[
|
|
567
|
+
Effect.broadcast(
|
|
568
|
+
'#foo',
|
|
569
|
+
[L('@+draft/typing=active :a!u@h TAGMSG #foo')],
|
|
570
|
+
'c0',
|
|
571
|
+
undefined,
|
|
572
|
+
undefined,
|
|
573
|
+
['message-tags', 'draft/typing'],
|
|
574
|
+
),
|
|
575
|
+
],
|
|
576
|
+
runtime,
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
expect(broadcasts).toHaveLength(0);
|
|
580
|
+
expect(sends.map((s) => s.to).sort()).toEqual(['c1', 'c2']);
|
|
581
|
+
expect(sends[0]?.lines).toEqual([L('@+draft/typing=active :a!u@h TAGMSG #foo')]);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('does NOT double-deliver to a member holding both caps', async () => {
|
|
585
|
+
const members = memberMap([['c1', { caps: new Set(['message-tags', 'draft/typing']) }]]);
|
|
586
|
+
const { runtime, sends } = capAwareRuntime(members);
|
|
587
|
+
|
|
588
|
+
await dispatch(
|
|
589
|
+
[
|
|
590
|
+
Effect.broadcast('#foo', [L('hi')], undefined, undefined, undefined, [
|
|
591
|
+
'message-tags',
|
|
592
|
+
'draft/typing',
|
|
593
|
+
]),
|
|
594
|
+
],
|
|
595
|
+
runtime,
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
expect(sends).toHaveLength(1);
|
|
599
|
+
expect(sends[0]?.to).toBe('c1');
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('honors `except` alongside `caps`', async () => {
|
|
603
|
+
const members = memberMap([
|
|
604
|
+
['c1', { caps: new Set(['message-tags']) }],
|
|
605
|
+
['c2', { caps: new Set(['draft/typing']) }],
|
|
606
|
+
]);
|
|
607
|
+
const { runtime, sends } = capAwareRuntime(members);
|
|
608
|
+
|
|
609
|
+
await dispatch(
|
|
610
|
+
[
|
|
611
|
+
Effect.broadcast('#foo', [L('hi')], 'c1', undefined, undefined, [
|
|
612
|
+
'message-tags',
|
|
613
|
+
'draft/typing',
|
|
614
|
+
]),
|
|
615
|
+
],
|
|
616
|
+
runtime,
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
expect(sends.map((s) => s.to)).toEqual(['c2']);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it('skips members whose connection info is unavailable (treats as no cap)', async () => {
|
|
623
|
+
const members = memberMap([['c1', { caps: new Set(['draft/typing']) }]]);
|
|
624
|
+
const { runtime, sends } = capAwareRuntime(members);
|
|
625
|
+
|
|
626
|
+
// Override getConnectionInfo to return null for c1 (connection gone).
|
|
627
|
+
const runtimeWithGone: IrcRuntime = {
|
|
628
|
+
...runtime,
|
|
629
|
+
async getConnectionInfo() {
|
|
630
|
+
return null;
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
await dispatch(
|
|
635
|
+
[Effect.broadcast('#foo', [L('hi')], undefined, undefined, undefined, ['draft/typing'])],
|
|
636
|
+
runtimeWithGone,
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
expect(sends).toHaveLength(0);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('is a no-op when the channel snapshot is null', async () => {
|
|
643
|
+
const sends: { to: ConnId; lines: RawLine[] }[] = [];
|
|
644
|
+
const runtime: IrcRuntime = {
|
|
645
|
+
async send(to, lines) {
|
|
646
|
+
sends.push({ to, lines });
|
|
647
|
+
},
|
|
648
|
+
async broadcast() {},
|
|
649
|
+
async broadcastWallops() {},
|
|
650
|
+
async disconnect() {},
|
|
651
|
+
async sendToNick() {},
|
|
652
|
+
async reserveNick() {
|
|
653
|
+
return { ok: true };
|
|
654
|
+
},
|
|
655
|
+
async changeNick() {
|
|
656
|
+
return true;
|
|
657
|
+
},
|
|
658
|
+
async releaseNick() {},
|
|
659
|
+
async applyChannelDelta() {},
|
|
660
|
+
async lookupNick() {
|
|
661
|
+
return null;
|
|
662
|
+
},
|
|
663
|
+
async getConnectionInfo() {
|
|
664
|
+
return null;
|
|
665
|
+
},
|
|
666
|
+
async getConnection() {
|
|
667
|
+
return null;
|
|
668
|
+
},
|
|
669
|
+
async getChannelSnapshot() {
|
|
670
|
+
return null;
|
|
671
|
+
},
|
|
672
|
+
async getChannelConnections() {
|
|
673
|
+
return new Map();
|
|
674
|
+
},
|
|
675
|
+
async listChannels() {
|
|
676
|
+
return [];
|
|
677
|
+
},
|
|
678
|
+
async reloadConfig() {
|
|
679
|
+
throw new Error('reloadConfig not configured for this fake');
|
|
680
|
+
},
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
await dispatch(
|
|
684
|
+
[Effect.broadcast('#nope', [L('x')], undefined, undefined, undefined, ['draft/typing'])],
|
|
685
|
+
runtime,
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
expect(sends).toHaveLength(0);
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// ===========================================================================
|
|
693
|
+
// dispatch — Broadcast client-tag gating (message-tags)
|
|
694
|
+
//
|
|
695
|
+
// Recipients that did NOT negotiate `message-tags` must never receive a line
|
|
696
|
+
// beginning with `@+<client-tag>`. The dispatch broadcast per-recipient path
|
|
697
|
+
// applies filterClientTags so draft/multiline's `+draft/multiline` joined
|
|
698
|
+
// PRIVMSG is stripped for legacy peers, while message-tags peers still see it.
|
|
699
|
+
// ===========================================================================
|
|
700
|
+
|
|
701
|
+
describe('dispatch — Broadcast client-tag gating', () => {
|
|
702
|
+
it('strips +draft/multiline for a non-message-tags recipient (split mode)', async () => {
|
|
703
|
+
const members = memberMap([
|
|
704
|
+
['c1', { caps: new Set(['draft/multiline']) }],
|
|
705
|
+
['c2', { caps: new Set<string>() }],
|
|
706
|
+
]);
|
|
707
|
+
const { runtime, sends } = capAwareRuntime(members);
|
|
708
|
+
|
|
709
|
+
await dispatch(
|
|
710
|
+
[
|
|
711
|
+
Effect.broadcast(
|
|
712
|
+
'#foo',
|
|
713
|
+
[L('@+draft/multiline :a!a@a PRIVMSG #foo :line one\nline two')],
|
|
714
|
+
'c0',
|
|
715
|
+
'draft/multiline',
|
|
716
|
+
[L('BATCH +b1 draft/multiline #foo'), L('BATCH -b1')],
|
|
717
|
+
),
|
|
718
|
+
],
|
|
719
|
+
runtime,
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
const toC2 = sends.find((s) => s.to === 'c2');
|
|
723
|
+
expect(toC2?.lines).toEqual([L(':a!a@a PRIVMSG #foo :line one\nline two')]);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it('preserves +draft/multiline for a message-tags recipient (split mode)', async () => {
|
|
727
|
+
const members = memberMap([
|
|
728
|
+
['c1', { caps: new Set(['draft/multiline']) }],
|
|
729
|
+
['c2', { caps: new Set(['message-tags']) }],
|
|
730
|
+
]);
|
|
731
|
+
const { runtime, sends } = capAwareRuntime(members);
|
|
732
|
+
|
|
733
|
+
await dispatch(
|
|
734
|
+
[
|
|
735
|
+
Effect.broadcast(
|
|
736
|
+
'#foo',
|
|
737
|
+
[L('@+draft/multiline :a!a@a PRIVMSG #foo :line one\nline two')],
|
|
738
|
+
'c0',
|
|
739
|
+
'draft/multiline',
|
|
740
|
+
[L('BATCH +b1 draft/multiline #foo'), L('BATCH -b1')],
|
|
741
|
+
),
|
|
742
|
+
],
|
|
743
|
+
runtime,
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
const toC2 = sends.find((s) => s.to === 'c2');
|
|
747
|
+
expect(toC2?.lines).toEqual([L('@+draft/multiline :a!a@a PRIVMSG #foo :line one\nline two')]);
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('leaves capLines (BATCH replay) untouched for multiline-cap recipients', async () => {
|
|
751
|
+
const members = memberMap([['c1', { caps: new Set(['draft/multiline', 'message-tags']) }]]);
|
|
752
|
+
const { runtime, sends } = capAwareRuntime(members);
|
|
753
|
+
|
|
754
|
+
await dispatch(
|
|
755
|
+
[
|
|
756
|
+
Effect.broadcast(
|
|
757
|
+
'#foo',
|
|
758
|
+
[L('@+draft/multiline :a!a@a PRIVMSG #foo :joined')],
|
|
759
|
+
'c0',
|
|
760
|
+
'draft/multiline',
|
|
761
|
+
[L('BATCH +b1 draft/multiline #foo'), L(':a!a@a PRIVMSG #foo :one'), L('BATCH -b1')],
|
|
762
|
+
),
|
|
763
|
+
],
|
|
764
|
+
runtime,
|
|
765
|
+
);
|
|
766
|
+
|
|
767
|
+
const toC1 = sends.find((s) => s.to === 'c1');
|
|
768
|
+
expect(toC1?.lines).toEqual([
|
|
769
|
+
L('BATCH +b1 draft/multiline #foo'),
|
|
770
|
+
L(':a!a@a PRIVMSG #foo :one'),
|
|
771
|
+
L('BATCH -b1'),
|
|
772
|
+
]);
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it('strips client tags in cap-only mode too (no capLines)', async () => {
|
|
776
|
+
const members = memberMap([
|
|
777
|
+
['c1', { caps: new Set(['message-tags']) }],
|
|
778
|
+
['c2', { caps: new Set<string>() }],
|
|
779
|
+
]);
|
|
780
|
+
const { runtime, sends } = capAwareRuntime(members);
|
|
781
|
+
|
|
782
|
+
await dispatch(
|
|
783
|
+
[Effect.broadcast('#foo', [L('@+draft/typing=active :a TAGMSG #foo')], 'c0', 'message-tags')],
|
|
784
|
+
runtime,
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
// c1 has message-tags → verbatim.
|
|
788
|
+
const toC1 = sends.find((s) => s.to === 'c1');
|
|
789
|
+
expect(toC1?.lines).toEqual([L('@+draft/typing=active :a TAGMSG #foo')]);
|
|
790
|
+
// c2 lacks message-tags AND the cap → receives nothing (cap-only mode).
|
|
791
|
+
const toC2 = sends.find((s) => s.to === 'c2');
|
|
792
|
+
expect(toC2).toBeUndefined();
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
|
|
535
796
|
// ===========================================================================
|
|
536
797
|
// dispatch observability
|
|
537
798
|
// ===========================================================================
|
|
@@ -25,3 +25,9 @@ export {
|
|
|
25
25
|
inMemoryWsHarnessFactory,
|
|
26
26
|
} from './in-memory-harness.js';
|
|
27
27
|
export { runIrcScenarios } from './scenarios.js';
|
|
28
|
+
export {
|
|
29
|
+
TEST_NETWORK_NAME,
|
|
30
|
+
TEST_SERVER_NAME,
|
|
31
|
+
makeTestServerConfig,
|
|
32
|
+
type TestServerConfigOverrides,
|
|
33
|
+
} from './test-config.js';
|
|
@@ -48,6 +48,21 @@ export function runIrcScenarios(factories: readonly IrcHarnessFactory[]): void {
|
|
|
48
48
|
return undefined;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Numeric error codes that PRIVMSG/NOTICE rejection paths would emit
|
|
53
|
+
* (401 nick-target only). RFC: NOTICE MUST NOT generate any of these.
|
|
54
|
+
* Used by the NOTICE scenario so a late-arriving MOTD/welcome numeric
|
|
55
|
+
* (e.g. 376) on a slow transport does not false-positive the check.
|
|
56
|
+
*/
|
|
57
|
+
const NOTICE_ERROR_CODES: ReadonlySet<string> = new Set([
|
|
58
|
+
'401', // ERR_NOSUCHNICK
|
|
59
|
+
'403', // ERR_NOSUCHCHANNEL
|
|
60
|
+
'404', // ERR_CANNOTSENDTOCHAN
|
|
61
|
+
'411', // ERR_NORECIPIENT
|
|
62
|
+
'412', // ERR_NOTEXTTOSEND
|
|
63
|
+
'442', // ERR_NOTONCHANNEL
|
|
64
|
+
]);
|
|
65
|
+
|
|
51
66
|
/**
|
|
52
67
|
* Synchronization helper: alice (already in #chan) waits until she sees
|
|
53
68
|
* bob's JOIN broadcast. By the time this resolves, the runtime has also
|
|
@@ -442,8 +457,15 @@ export function runIrcScenarios(factories: readonly IrcHarnessFactory[]): void {
|
|
|
442
457
|
bob.received.length = 0;
|
|
443
458
|
await alice.send('NOTICE #notice :quiet note');
|
|
444
459
|
await bob.waitForLine((l) => l.includes('NOTICE #notice :quiet note'));
|
|
445
|
-
|
|
446
|
-
|
|
460
|
+
// RFC: NOTICE MUST NOT generate an error reply. Check only the
|
|
461
|
+
// NOTICE-relevant error numerics — not any numeric — so a delayed
|
|
462
|
+
// MOTD/welcome line draining through a slow transport (e.g. the CF
|
|
463
|
+
// WebSocket pool under load) does not false-positive the assertion.
|
|
464
|
+
const noticeErrors = alice.received.filter((l) => {
|
|
465
|
+
const code = numericOf(l);
|
|
466
|
+
return code !== undefined && NOTICE_ERROR_CODES.has(code);
|
|
467
|
+
});
|
|
468
|
+
expect(noticeErrors).toEqual([]);
|
|
447
469
|
await harness.close();
|
|
448
470
|
});
|
|
449
471
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-only server-config builder.
|
|
3
|
+
*
|
|
4
|
+
* Production config no longer ships a baseline `serverName` — every
|
|
5
|
+
* adapter's loader fails fast when the knob is missing. Test code that
|
|
6
|
+
* previously spread `DEFAULT_SERVER_CONFIG` needs an explicit, clearly
|
|
7
|
+
* test-scoped substitute so a forgotten override never silently advertises
|
|
8
|
+
* a placeholder hostname on the wire.
|
|
9
|
+
*
|
|
10
|
+
* The helper funnels its result through the shared `parseServerConfig` so
|
|
11
|
+
* the object shape stays schema-valid even when a test overrides
|
|
12
|
+
* individual knobs.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { type ParsedServerConfig, parseServerConfig } from '@serverless-ircd/irc-core';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Input accepted by {@link makeTestServerConfig}: any subset of the
|
|
19
|
+
* schema's input shape. Fields the caller omits fall back to the
|
|
20
|
+
* test-only baseline.
|
|
21
|
+
*/
|
|
22
|
+
export type TestServerConfigOverrides = Partial<Parameters<typeof parseServerConfig>[0]> &
|
|
23
|
+
Record<string, unknown>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Test-only baseline hostname. Deliberately not `irc.example.com` (the
|
|
27
|
+
* retired placeholder) so any assertion that catches the placeholder
|
|
28
|
+
* leaking into production stays loud.
|
|
29
|
+
*/
|
|
30
|
+
export const TEST_SERVER_NAME = 'irc.test.local';
|
|
31
|
+
|
|
32
|
+
/** Test-only baseline network name. */
|
|
33
|
+
export const TEST_NETWORK_NAME = 'TestNet';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Builds a fresh, schema-valid `ParsedServerConfig` for tests.
|
|
37
|
+
*
|
|
38
|
+
* Callers may omit `serverName`/`networkName` entirely — the helper
|
|
39
|
+
* supplies a test-only baseline that round-trips through
|
|
40
|
+
* `parseServerConfig`. Overrides are merged before parsing so the result
|
|
41
|
+
* always satisfies the schema (a bad override surfaces as a throw, same
|
|
42
|
+
* as a malformed production config).
|
|
43
|
+
*
|
|
44
|
+
* Each call returns an independent object; mutating the result never
|
|
45
|
+
* bleeds into subsequent calls.
|
|
46
|
+
*/
|
|
47
|
+
export function makeTestServerConfig(overrides?: TestServerConfigOverrides): ParsedServerConfig {
|
|
48
|
+
const input: Record<string, unknown> = {
|
|
49
|
+
serverName: TEST_SERVER_NAME,
|
|
50
|
+
networkName: TEST_NETWORK_NAME,
|
|
51
|
+
...overrides,
|
|
52
|
+
};
|
|
53
|
+
return parseServerConfig(input);
|
|
54
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the `makeTestServerConfig` test helper.
|
|
3
|
+
*
|
|
4
|
+
* The helper exists so test code can build a valid `ParsedServerConfig`
|
|
5
|
+
* without sourcing `serverName` from the production default constant
|
|
6
|
+
* (which no longer carries one). Every adapter's integration suite
|
|
7
|
+
* spreads this baseline; the helper guarantees the result still parses
|
|
8
|
+
* cleanly through the shared schema.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type ParsedServerConfig, parseServerConfig } from '@serverless-ircd/irc-core';
|
|
12
|
+
import { describe, expect, it } from 'vitest';
|
|
13
|
+
import { makeTestServerConfig } from '../src/test-config.js';
|
|
14
|
+
|
|
15
|
+
describe('makeTestServerConfig', () => {
|
|
16
|
+
it('returns a config whose serverName is not the public placeholder', () => {
|
|
17
|
+
const cfg = makeTestServerConfig();
|
|
18
|
+
expect(cfg.serverName).not.toBe('irc.example.com');
|
|
19
|
+
expect(cfg.serverName.length).toBeGreaterThan(0);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('returns a config that round-trips through parseServerConfig', () => {
|
|
23
|
+
const cfg = makeTestServerConfig();
|
|
24
|
+
const reparsed = parseServerConfig(cfg);
|
|
25
|
+
expect(reparsed).toEqual(cfg);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('supplies a non-empty networkName by default', () => {
|
|
29
|
+
const cfg = makeTestServerConfig();
|
|
30
|
+
expect(cfg.networkName.length).toBeGreaterThan(0);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('lets the caller override serverName with an explicit test value', () => {
|
|
34
|
+
const cfg = makeTestServerConfig({ serverName: 'irc.test.local' });
|
|
35
|
+
expect(cfg.serverName).toBe('irc.test.local');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('lets the caller override arbitrary knobs without dropping the baseline', () => {
|
|
39
|
+
const cfg = makeTestServerConfig({ maxClients: 7, nickLen: 9 });
|
|
40
|
+
expect(cfg.maxClients).toBe(7);
|
|
41
|
+
expect(cfg.nickLen).toBe(9);
|
|
42
|
+
expect(cfg.serverName.length).toBeGreaterThan(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('returns a fresh object each call (no shared mutable baseline)', () => {
|
|
46
|
+
const a = makeTestServerConfig() as ParsedServerConfig & { motdLines: string[] };
|
|
47
|
+
const b = makeTestServerConfig() as ParsedServerConfig & { motdLines: string[] };
|
|
48
|
+
a.motdLines.push('mutated');
|
|
49
|
+
expect(b.motdLines).not.toContain('mutated');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI capability self-checks for DynamoDB Local.
|
|
3
|
+
*
|
|
4
|
+
* The `aws-adapter` package's `globalSetup` boots a real DynamoDB Local
|
|
5
|
+
* instance so the integration suite (handlers/default.ts,
|
|
6
|
+
* handlers/nlb-stream.ts, aws-runtime.ts, …) can be exercised under
|
|
7
|
+
* coverage. Without a DynamoDB Local backend, 171 of 294 tests
|
|
8
|
+
* `describe.skipIf` away and `aws-adapter` coverage collapses to ~42%,
|
|
9
|
+
* blowing the 90% gate.
|
|
10
|
+
*
|
|
11
|
+
* These tests assert that the CI workflow wires the runner capability
|
|
12
|
+
* the globalSetup needs (a JRE for the official ZIP fallback) and that
|
|
13
|
+
* the strategy is pinned deterministically so the testcontainers
|
|
14
|
+
* probe does not burn ~60s of CI time before falling back.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
import { resolve } from 'node:path';
|
|
19
|
+
import { describe, expect, it } from 'vitest';
|
|
20
|
+
|
|
21
|
+
const REPO_ROOT = resolve(import.meta.dirname, '..', '..', '..');
|
|
22
|
+
|
|
23
|
+
function repoPath(rel: string): string {
|
|
24
|
+
return resolve(REPO_ROOT, rel);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readText(rel: string): string {
|
|
28
|
+
return readFileSync(repoPath(rel), 'utf8');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('DynamoDB Local CI capability', () => {
|
|
32
|
+
it('installs a JRE on the CI runner so the ZIP fallback can fire', () => {
|
|
33
|
+
// The self-hosted Gitea runner image ships with neither Docker nor
|
|
34
|
+
// Java, so the globalSetup's testcontainers strategy fails fast
|
|
35
|
+
// ("Failed to connect to Reaper") and the ZIP strategy bails on
|
|
36
|
+
// `which('java') === null`. Installing a JRE via `setup-java`
|
|
37
|
+
// unblocks the ZIP path without requiring privileged mode or a
|
|
38
|
+
// `docker.sock` mount.
|
|
39
|
+
const ci = readText('.github/workflows/ci.yml');
|
|
40
|
+
expect(ci).toMatch(/setup-java/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('pins the DynamoDB Local strategy to ZIP in CI to skip the testcontainers probe', () => {
|
|
44
|
+
// testcontainers' Ryuk reaper container cannot start without a
|
|
45
|
+
// privileged runner / `docker.sock` mount; the probe blocks for
|
|
46
|
+
// tens of seconds (timeout) before falling back, which is flaky
|
|
47
|
+
// and slow. Pinning `DDB_LOCAL_STRATEGY=zip` on the coverage step
|
|
48
|
+
// makes globalSetup skip the Docker attempt entirely.
|
|
49
|
+
const ci = readText('.github/workflows/ci.yml');
|
|
50
|
+
expect(ci).toMatch(/DDB_LOCAL_STRATEGY/);
|
|
51
|
+
expect(ci).toMatch(/DDB_LOCAL_STRATEGY.*zip|zip.*DDB_LOCAL_STRATEGY/s);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('documents the DynamoDB Local capability requirement in workflow comments', () => {
|
|
55
|
+
// The next contributor who touches the coverage job needs to know
|
|
56
|
+
// why Java is installed and why the strategy is pinned, so the
|
|
57
|
+
// capability is not silently dropped during a refactor.
|
|
58
|
+
const ci = readText('.github/workflows/ci.yml');
|
|
59
|
+
expect(ci).toMatch(/DynamoDB Local/i);
|
|
60
|
+
expect(ci).toMatch(/ZIP|Java/i);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('globalSetup strategy pinning', () => {
|
|
65
|
+
it('honors DDB_LOCAL_STRATEGY to skip the testcontainers attempt', () => {
|
|
66
|
+
// The globalSetup reads `DDB_LOCAL_STRATEGY` and short-circuits the
|
|
67
|
+
// Docker probe when set to `zip`, and the ZIP probe when set to
|
|
68
|
+
// `docker`. Default (`auto`) tries both for local dev parity.
|
|
69
|
+
const setup = readText('packages/aws-adapter/tests/global-setup.ts');
|
|
70
|
+
expect(setup).toMatch(/DDB_LOCAL_STRATEGY/);
|
|
71
|
+
// The two real strategies must be selectable explicitly.
|
|
72
|
+
expect(setup).toMatch(/['"]zip['"]/);
|
|
73
|
+
expect(setup).toMatch(/['"]docker['"]/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('still allows `auto` (both strategies) for local development', () => {
|
|
77
|
+
// The default behavior — try Docker, then ZIP — must remain so a
|
|
78
|
+
// developer with Docker Desktop still gets the testcontainers path.
|
|
79
|
+
const setup = readText('packages/aws-adapter/tests/global-setup.ts');
|
|
80
|
+
expect(setup).toMatch(/auto/);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@serverless-ircd/load-test",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Synthetic WebSocket IRC client farm for load testing ServerlessIRCd (concurrency, latency p50/p95/p99, drop rate)",
|
|
6
|
+
"license": "BSD-3-Clause",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/harness.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"load-test": "./dist/main.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc -p tsconfig.build.json",
|
|
14
|
+
"typecheck": "tsc -p tsconfig.test.json --noEmit",
|
|
15
|
+
"start": "node --enable-source-maps ./dist/main.js",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"coverage": "vitest run --coverage",
|
|
19
|
+
"clean": "rimraf dist coverage .tsbuildinfo .turbo"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ws": "^8.18.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@serverless-ircd/local-cli": "workspace:*",
|
|
26
|
+
"@types/node": "^26.1.1",
|
|
27
|
+
"@types/ws": "^8.5.13",
|
|
28
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
29
|
+
"rimraf": "^6.0.0",
|
|
30
|
+
"typescript": "^5.6.0",
|
|
31
|
+
"vitest": "^4.1.0"
|
|
32
|
+
}
|
|
33
|
+
}
|