From 1e98d700630755eb8607d3af69551b089e017455 Mon Sep 17 00:00:00 2001 From: Calic Date: Sun, 14 Jun 2026 01:26:36 +0200 Subject: [PATCH] =?UTF-8?q?initial:=20notify-test=20v0.1.0=20=E2=80=94=201?= =?UTF-8?q?3=20assertions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests: channel create/list round-trip, tag-match routing, non-match exclusion, catch-all (empty filter), subscribe callback, unsubscribe, post-without-tags error, empty-tags error, duplicate-channel error, unknown-display-mode error, ring-buffer cap, engine-log-mirror toggle, and severity sugar (warn/info). --- LICENSE | 24 +++++++++ init.lua | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++ manifest.core | 6 +++ manifest.lib | 9 ++++ 4 files changed, 186 insertions(+) create mode 100644 LICENSE create mode 100644 init.lua create mode 100644 manifest.core create mode 100644 manifest.lib diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..81c9d65 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2026 Calic. All rights reserved. + +This software is part of the Sporel platform — **Tier 1 (Official / +Proprietary)** content per the Three-Tier Licensing Model documented in +`meta/docs/archive/design/vision.md §Licensing Model` (current source; +migration to `meta/docs/architecture/licensing-model.md` pending). + +⚠ **WIP — Legal review required before public launch.** The terms below +reflect design intent only; the formalized license framework will be +finalized through legal counsel before the first public release. Until +then, this notice serves as a placeholder defending the platform owner's +rights against unintentional re-licensing. + +No license is granted to copy, modify, distribute, sublicense, or otherwise +use this software in any form without prior written permission from the +copyright holder. + +References: +- Tier 1 (this file): all rights reserved, proprietary, sold/distributed + via official channels (Steam, etc.) +- Tier 2 (Semi-Commercial Co-Development): bilateral contracts, revenue- + share — see vision.md §Licensing Model +- Tier 3 (Community Content): CC BY-NC-SA 4.0 + asymmetric CLA — applies + to community-uploaded libs/modules/assets, not this repo diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..11da7e5 --- /dev/null +++ b/init.lua @@ -0,0 +1,147 @@ +-- lib-core.notify-test v0.1.0 — 29 assertions +-- Exercises: channel create/list, tag routing, catch-all, subscribe/unsubscribe, +-- error cases, ring-buffer, log-mirror toggle, severity sugar. + +local notify = require("lib-core.notify") +local T = engine.test + +local M = {} + +function M.run_tests(_ctx) + notify._test_clear_all() + + -- ------------------------------------------------------------------ + -- 1. create + list round-trip + -- ------------------------------------------------------------------ + notify.create_channel{ id = "c1", filter = {"a"}, display_mode = "log" } + local list = notify.list_channels() + T.equals(#list, 1, "list_channels returns 1 channel after create") + T.equals(list[1].id, "c1", "listed channel id matches") + + -- ------------------------------------------------------------------ + -- 2. matching post adds to channel history + -- ------------------------------------------------------------------ + notify.post{ tags = {"a"}, text = "hello" } + local h = notify.get_history("c1") + T.equals(#h, 1, "matching post adds message to channel history") + T.equals(h[1].text, "hello", "message text is preserved in history") + + -- ------------------------------------------------------------------ + -- 3. non-matching post does NOT add to channel history + -- ------------------------------------------------------------------ + notify.post{ tags = {"b"}, text = "ignored" } + h = notify.get_history("c1") + T.equals(#h, 1, "non-matching post does not add to channel history") + + -- ------------------------------------------------------------------ + -- 4. empty filter = catch-all + -- ------------------------------------------------------------------ + notify.create_channel{ id = "catch_all", filter = {}, display_mode = "log" } + notify.post{ tags = {"x", "y", "z"}, text = "broadcast" } + h = notify.get_history("catch_all") + T.equals(#h, 1, "empty filter catches any-tagged message") + + -- ------------------------------------------------------------------ + -- 5. subscribe callback fires on matching post + -- ------------------------------------------------------------------ + notify.create_channel{ id = "sub_ch", filter = {"ev"}, display_mode = "toast" } + local fired = 0 + local last_msg = nil + local handle = notify.subscribe("sub_ch", function(msg) + fired = fired + 1 + last_msg = msg + end) + notify.post{ tags = {"ev"}, text = "event!" } + T.equals(fired, 1, "subscribe callback fires once on matching post") + T.equals(last_msg.text, "event!", "callback receives correct message") + + -- ------------------------------------------------------------------ + -- 6. unsubscribe stops callback + -- ------------------------------------------------------------------ + notify.unsubscribe(handle) + notify.post{ tags = {"ev"}, text = "after unsub" } + T.equals(fired, 1, "unsubscribed callback does not fire on subsequent post") + T.equals(#notify._test_get_subscribers("sub_ch"), 0, + "subscriber list is empty after unsubscribe") + + -- ------------------------------------------------------------------ + -- 7. post without tags field → loud-error + -- ------------------------------------------------------------------ + local ok_no_tags = pcall(notify.post, { text = "no tags" }) + T.assert(not ok_no_tags, "post without tags field raises an error") + + -- ------------------------------------------------------------------ + -- 8. post with empty tags array → loud-error + -- ------------------------------------------------------------------ + local ok_empty_tags = pcall(notify.post, { tags = {}, text = "empty tags" }) + T.assert(not ok_empty_tags, "post with empty tags array raises an error") + + -- ------------------------------------------------------------------ + -- 9. duplicate channel id → loud-error + -- ------------------------------------------------------------------ + local ok_dup = pcall(notify.create_channel, + { id = "c1", filter = {}, display_mode = "log" }) + T.assert(not ok_dup, "create_channel with duplicate id raises an error") + + -- ------------------------------------------------------------------ + -- 10. unknown display_mode → loud-error + -- ------------------------------------------------------------------ + local ok_bad_mode = pcall(notify.create_channel, + { id = "bad_mode_ch", filter = {}, display_mode = "popup" }) + T.assert(not ok_bad_mode, "create_channel with unknown display_mode raises an error") + + -- ------------------------------------------------------------------ + -- 11. max_history ring-buffer + -- ------------------------------------------------------------------ + notify.create_channel{ id = "ring", filter = {"r"}, display_mode = "toast", max_history = 3 } + for i = 1, 5 do + notify.post{ tags = {"r"}, text = "msg" .. i } + end + h = notify.get_history("ring") + T.equals(#h, 3, "ring-buffer caps history at max_history (3)") + T.equals(h[1].text, "msg3", "oldest kept entry is msg3 (msg1+msg2 dropped)") + T.equals(h[3].text, "msg5", "newest entry is msg5") + + -- ------------------------------------------------------------------ + -- 12. set_engine_log_mirror toggle + -- ------------------------------------------------------------------ + T.assert(notify._test_get_log_mirror() == true, "engine_log_mirror is true by default") + notify.set_engine_log_mirror(false) + T.assert(notify._test_get_log_mirror() == false, "set_engine_log_mirror(false) disables mirror") + notify.set_engine_log_mirror(true) + T.assert(notify._test_get_log_mirror() == true, "set_engine_log_mirror(true) re-enables mirror") + -- non-bool truthy does NOT enable mirror + notify.set_engine_log_mirror("yes") + T.assert(notify._test_get_log_mirror() == false, + "set_engine_log_mirror with non-bool truthy does not enable mirror") + + -- ------------------------------------------------------------------ + -- 13. severity sugar: warn/info set correct severity field + -- ------------------------------------------------------------------ + notify.create_channel{ id = "sev", filter = {"s"}, display_mode = "log" } + notify.warn({"s"}, "a warning") + notify.info({"s"}, "an info") + h = notify.get_history("sev") + T.equals(#h, 2, "warn + info each post one message") + T.equals(h[1].severity, "warn", "warn() sets severity to 'warn'") + T.equals(h[2].severity, "info", "info() sets severity to 'info'") + + -- ------------------------------------------------------------------ + -- 14. post() does not mutate caller's table + -- ------------------------------------------------------------------ + notify._test_clear_all() + notify.create_channel{ id = "x", filter = {"x"}, display_mode = "log" } + local m = { tags = {"x"}, text = "first" } + notify.post(m) + T.assert(m.id == nil, "post does not mutate caller msg.id") + T.assert(m.timestamp == nil, "post does not mutate caller msg.timestamp") + -- Re-using the same table for a second post + m.text = "second" + notify.post(m) + h = notify.get_history("x") + T.assert(#h == 2, "two posts store as two distinct history entries") + T.equals(h[1].text, "first", "first history entry preserved") + T.equals(h[2].text, "second", "second history entry distinct") +end + +return M diff --git a/manifest.core b/manifest.core new file mode 100644 index 0000000..98ffdad --- /dev/null +++ b/manifest.core @@ -0,0 +1,6 @@ +{ + "id": "lib-core.notify-test.core", + "kind": "lib", + "version": "0.1.0", + "license": "Proprietary" +} diff --git a/manifest.lib b/manifest.lib new file mode 100644 index 0000000..e74c916 --- /dev/null +++ b/manifest.lib @@ -0,0 +1,9 @@ +{ + "id": "lib-core.notify-test", + "role": "test", + "version": "0.1.0", + "api_min": "0.1", + "deps": [ + {"id": "lib-core.notify", "version": "0.1.0"} + ] +}