From 05094c5e241fb192eeb6a4df70403d4cb23c856b Mon Sep 17 00:00:00 2001 From: Axel Meyer Date: Tue, 9 Jun 2026 13:28:55 +0000 Subject: [PATCH] =?UTF-8?q?initial:=20composition-test=20v0.1.0=20?= =?UTF-8?q?=E2=80=94=2014=20assertions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase-A.1 test-lib for lib-core.composition. Covers define_template, create, list_by_template, list_by_tag, destroy, list_templates. Co-Authored-By: Claude Opus 4.7 (1M context) --- LICENSE | 24 +++++++++++ README.md | 33 +++++++++++++++ init.lua | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++ manifest.core | 6 +++ manifest.lib | 9 ++++ 5 files changed, 183 insertions(+) create mode 100644 LICENSE create mode 100644 README.md 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/README.md b/README.md new file mode 100644 index 0000000..43d5be7 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# lib-core.composition-test + +Test-lib for `lib-core.composition` v0.1.0. Follows the P.2.4 +Test-Module-Pattern: exports `run_tests(ctx)` invoked by the engine's +`--test-libs` runner; uses `engine.test.*` for TAP-style output. + +**Version:** 0.1.0 +**Lib-ID:** lib-core.composition-test +**Role:** test +**Requires:** lib-core.composition v0.1.0 +**Tags:** test, composition + +## Coverage (14 assertions) + +| Group | Count | What | +|---|---|---| +| `define_template` | 4 | happy-path register, missing-id error, duplicate error, slot-block error (Phase-D trigger) | +| `create` | 4 | returns non-nil handle, override applies, nested-flatten works, template-default survives | +| `list_by_*` | 3 | template-list count, tag-list count, unknown-template empty | +| `destroy` | 2 | template-index grows on second create, shrinks on destroy | +| `list_templates` | 1 | non-empty after define | + +## Run + +Headless via the engine's test-libs orchestrator: + +```bash +~/Projects/Sporel/sporel-engine/build/sporel \ + --test-libs=lib-core.composition-test +``` + +(or however the local dev convention runs `engine.test.*` lib-tests — +see `engine/scripts/run-tests.sh` for the canonical invocation). diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..8d7861d --- /dev/null +++ b/init.lua @@ -0,0 +1,111 @@ +-- lib-core.composition-test — Phase A.1 +-- 14 assertions: 4 define_template + 4 create + 3 list_by_* + 2 destroy + 1 list_templates. +-- Pattern follows P.2.4 Test-Module-Pattern; TAP output via engine.test.* + +local composition = require("lib-core.composition") +local T = engine.test + +local M = {} + +function M.run_tests(ctx) + -- ---------------------------------------------------------------- + -- define_template (4) + -- ---------------------------------------------------------------- + + -- Happy path: define + retrieve via list_templates + composition.define_template{ + id = "sign", + properties = { + sprite_path = "sprites/sign.png", + text = "default text", + position = {x = 0, y = 0}, + }, + tags = {"renderable"}, + } + local templates_after_define = composition.list_templates() + local found_sign = false + for _, id in ipairs(templates_after_define) do + if id == "sign" then found_sign = true; break end + end + T.assert(found_sign, "define_template 'sign' registers in list_templates") + + -- Loud-error on missing id + local ok_missing = pcall(composition.define_template, { properties = {} }) + T.assert(not ok_missing, "define_template loud-errors on missing id") + + -- Loud-error on duplicate id (sign already defined above) + local ok_dup = pcall(composition.define_template, { id = "sign", properties = {} }) + T.assert(not ok_dup, "define_template loud-errors on duplicate id") + + -- Loud-error on deferred slots-block (Phase D) + local ok_slots = pcall(composition.define_template, { + id = "sword", + slots = { blade = {} }, + }) + T.assert(not ok_slots, "define_template loud-errors on 'slots' block (Phase D trigger)") + + -- ---------------------------------------------------------------- + -- create (4) + -- ---------------------------------------------------------------- + + -- Happy path: create returns a non-nil entity that carries template + -- properties + per-instance overrides + local sign1 = composition.create{ + template = "sign", + properties = { + position = {x = 144, y = 200}, + text = "Hello", + }, + } + T.assert(sign1 ~= nil, "create returns a non-nil entity handle") + T.equals(sign1:get_property("text"), "Hello", + "create override sets text-property") + T.equals(sign1:get_property("position.x"), 144, + "create flattens nested position into position.x") + + -- Template-default-property survives when override doesn't touch it + T.equals(sign1:get_property("sprite_path"), "sprites/sign.png", + "create preserves template default for sprite_path") + + -- ---------------------------------------------------------------- + -- list_by_template + list_by_tag (3) + -- ---------------------------------------------------------------- + + -- Single instance + local by_tpl = composition.list_by_template("sign") + T.assert(#by_tpl == 1, "list_by_template returns 1 entity after 1 create") + + -- Tag-Index + local by_tag = composition.list_by_tag("renderable") + T.assert(#by_tag == 1, "list_by_tag returns 1 entity for 'renderable'") + + -- Unknown template / tag → empty + local empty_tpl = composition.list_by_template("nonexistent") + T.assert(#empty_tpl == 0, "list_by_template empty for unknown template") + + -- ---------------------------------------------------------------- + -- destroy (2) + -- ---------------------------------------------------------------- + + -- Create a second instance, then destroy it, indexes shrink + local sign2 = composition.create{ + template = "sign", + properties = { position = {x = 50, y = 50}, text = "T2" }, + } + T.assert(#composition.list_by_template("sign") == 2, + "list_by_template grows to 2 after second create") + + composition.destroy(sign2) + T.assert(#composition.list_by_template("sign") == 1, + "destroy shrinks template-index back to 1") + + -- ---------------------------------------------------------------- + -- list_templates (1) + -- ---------------------------------------------------------------- + + -- list_templates is non-empty after define + T.assert(#composition.list_templates() >= 1, + "list_templates returns at least 1 entry after define_template") +end + +return M diff --git a/manifest.core b/manifest.core new file mode 100644 index 0000000..9aa597d --- /dev/null +++ b/manifest.core @@ -0,0 +1,6 @@ +{ + "id": "lib-core.composition-test.core", + "kind": "lib", + "version": "0.1.0", + "license": "Proprietary" +} diff --git a/manifest.lib b/manifest.lib new file mode 100644 index 0000000..bf20260 --- /dev/null +++ b/manifest.lib @@ -0,0 +1,9 @@ +{ + "id": "lib-core.composition-test", + "role": "test", + "version": "0.1.0", + "api_min": "0.1", + "deps": [ + {"id": "lib-core.composition", "version": "0.1.0"} + ] +}