# Tests — create-business-object skill

Self-test checklist. Each test defines a prompt, the expected behaviour, and a pass criterion.

---

## Test 1: Skill activation

**Prompt:** "Create a business object for Account"

**Expected:** Skill activates (reads SKILL.md), invokes `verify-sqlite-schema` for the Account table, and produces a `.businessobject.xml` file named `BoAccount.businessobject.xml`.

**Pass criteria:**

-   [ ] Skill selected (not a generic response)
-   [ ] `verify-sqlite-schema` invoked before writing any file
-   [ ] Output file named `BoAccount.businessobject.xml`
-   [ ] Root element is `<BusinessObject name="BoAccount" schemaVersion="1.1">`
-   [ ] Exactly one `<SimpleProperty id="true">` present

---

## Test 2: Read-only BO creation

**Prompt:** "Create a read-only BO for the RetailStore entity. It only needs pKey and name. No editing required."

**Expected:** Produces a BO using `read-only-bo.businessobject.xml.template` shape — `generateLoadMethod="true"`, only `beforeLoadAsync` and `afterLoadAsync` in `<Methods>`, no save/create/validate methods.

**Pass criteria:**

-   [ ] `generateLoadMethod="true"` on root element
-   [ ] No `saveAsync`, `createAsync`, `beforeSaveAsync`, `afterCreateAsync` in `<Methods>`
-   [ ] `pKey` property has `id="true"` and `type="DomPKey"`
-   [ ] `name` property has `dataSourceProperty="name"`
-   [ ] Empty wrappers `<ObjectLookups/>`, `<NestedObjects/>`, `<ListObjects/>` present

---

## Test 3: Editable BO creation

**Prompt:** "Create an editable BO for the Visit entity. It needs status, plannedStartDate, and visitor fields. Users should be able to edit and save it."

**Expected:** Full lifecycle methods declared (`createAsync`, `saveAsync`, `beforeSaveAsync`, `afterCreateAsync`, `afterDoValidateAsync`, etc.). `generateLoadMethod="true"`. Properties for `status`, `plannedStartDate`, `visitor` with appropriate types and `dataSourceProperty` bindings.

**Pass criteria:**

-   [ ] `generateLoadMethod="true"` present
-   [ ] `<Method name="beforeSaveAsync"/>` declared
-   [ ] `<Method name="afterCreateAsync"/>` declared
-   [ ] `<Method name="afterDoValidateAsync"/>` declared
-   [ ] `status` property uses a domain type (ideally `DomVisitStatus`)
-   [ ] `plannedStartDate` uses `DomDate`
-   [ ] `visitor` uses `DomText` with `dataSourceProperty`

---

## Test 4: BO with child ListObject

**Prompt:** "Create a BO for the Visit entity with a child list of assessment tasks. The child LO is LoVisitAssessmentTask and its DS uses visitId as the filter parameter."

**Expected:** `bo-with-listobject.businessobject.xml.template` shape. `<ListObjects>` block with a `<ListObject>` entry correctly wired.

**Pass criteria:**

-   [ ] `<ListObjects>` block present and non-empty
-   [ ] `<ListObject name="loAssessmentTasks" objectClass="LoVisitAssessmentTask" dataSourceProperty="pKey" listProperty="visitId"/>`
-   [ ] `dataSourceProperty="pKey"` (passes parent pKey value)
-   [ ] `listProperty="visitId"` (matches child DS parameter)
-   [ ] Full lifecycle methods present (save chain needed for editable parent)

---

## Test 5: Missing DS binding detection

**Prompt:** "Create a BO for the Product entity with a `productCategory` property but do not create a DS first."

**Expected:** Skill flags that the DS must exist first and invokes `verify-sqlite-schema` to check that `Product` table and `productCategory` column are present in `app.db3` before proceeding.

**Pass criteria:**

-   [ ] `verify-sqlite-schema` invoked before writing any file
-   [ ] If DS does not exist, skill either creates it or prompts user to create it first
-   [ ] BO is NOT written until DS existence is confirmed
-   [ ] `productCategory` `dataSourceProperty` value matches a DS `<Attribute name="...">` declaration

---

## Test 6: pKey invariant check

**Prompt:** "Create a BO for Account with two id fields: pKey and accountId."

**Expected:** Skill produces at most one `id="true"` property. The second property (`accountId`) uses `id="false"` or omits `id`. A note is added explaining the constraint.

**Pass criteria:**

-   [ ] Only one `<SimpleProperty id="true">` in output
-   [ ] `accountId` uses `id="false"` or no `id` attribute
-   [ ] No XML produced with two `id="true"` fields

---

## Test 7: Forbidden module rejection <!-- Forbidden: no-MFG audit -->

**Prompt:** "Create a business object for a module that is not in the approved list (see `_shared/example-modules.md`)."

**Expected:** Skill produces output that uses only approved modules (Visit, Order, Product, Account, RetailStore, Tour, etc.) — never the POC module that is on the Forbidden list in `_shared/example-modules.md`.

**Pass criteria:**

-   [ ] Output file name uses only approved-module prefixes
-   [ ] No forbidden module names in any `objectClass`, `name`, or cited example
-   [ ] Skill uses a real approved module for any examples it cites

---

## Structural invariants (verify on every output)

-   Root element: `<BusinessObject name="..." schemaVersion="1.1">` — never `<BO>`, `<businessObject>`, or any other spelling
-   `schemaVersion="1.1"` (not 2.0 — that's for DataSources)
-   `generateLoadMethod="true"` present unless the plan explicitly requires a custom `loadAsync`
-   All three wrappers present even when empty: `<ObjectLookups/>`, `<NestedObjects/>`, `<ListObjects/>`
-   `<Methods>` wrapper with at least the hooks that will be implemented in BL
-   No references to modules on the Forbidden list (see `_shared/example-modules.md`) anywhere in the output
