initial: composition-test v0.1.0 — 14 assertions
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) <noreply@anthropic.com>
This commit is contained in:
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -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
|
||||||
33
README.md
Normal file
33
README.md
Normal file
@@ -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).
|
||||||
111
init.lua
Normal file
111
init.lua
Normal file
@@ -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
|
||||||
6
manifest.core
Normal file
6
manifest.core
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": "lib-core.composition-test.core",
|
||||||
|
"kind": "lib",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "Proprietary"
|
||||||
|
}
|
||||||
9
manifest.lib
Normal file
9
manifest.lib
Normal file
@@ -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"}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user