Skip to content

fix(validate): reject selects whose answer options can't be resolved - #42

Merged
jstet merged 1 commit into
mainfrom
fix/unresolvable-choice-lists
Sep 24, 2026
Merged

jstet merged 1 commit into
mainfrom
fix/unresolvable-choice-lists

Conversation

@jstet

@jstet jstet commented Sep 24, 2026

Copy link
Copy Markdown
Member

Closes #41 (reported from formulaid, which relies on validateSubset as its only gate before download).

type cell before: validateSubset / convert after
select_one skala, no rows for skala [] / L with 0 answers error: list "skala" has no rows on the choices sheet
select_one (no list) [] / L with 0 answers error: needs a choice list
select_one_from_file foo.csv (unregistered) [] / "Unimplemented XLSForm type" error: "foo.csv" is not a registered vocabulary (registered: iso_3166_1.csv)
select_one_from_file (no file) [] / same throw error: needs a vocabulary file
select_one_from_file iso_3166_1.csv [] / 249 answers unchanged

The select_multiple variants behave the same way.

  • validateSubset(survey, choices, { fileChoices }): the new optional argument makes CSVs the caller will pass to convert() count as resolved. SubsetOptions is exported.
  • formtransform validate passes the CSVs beside the form, as xlsform2lstsv already does. Checked by hand: an unregistered own.csv is an error until it's placed beside the workbook.
  • The converter throws for the same cases, with messages naming the question and what's missing. It no longer says "Unimplemented type" for a supported type with a missing file.

Fixture fix: validation_relevance_survey's consent was one of these cases. It put its list in a non-standard list_name survey column, so its blessed snapshot froze an L question with 0 answers. It now reads select_one yes_no, and the re-blessed TSV/DDI gain its two answers.

Tests: new validateSubset cases for every row in the table. The two "handles missing choice list gracefully" tests now expect the error.

  • vitest: 838 passed. npm run validate is clean.
  • Full live suite: 115 passed, 1 xfailed (note).
  • pytest: 37 passed.

🤖 Generated with Claude Code

validateSubset returned [] for four cases that then converted to a broken
question or failed with a misleading error (reported from formulaid, which
uses validateSubset as its only gate):

- select_one / select_multiple with no list name, or a list with no rows
  on the choices sheet → LimeSurvey L/M question with 0 answers
- select_*_from_file with no file, or a file that isn't a registered
  vocabulary → "Unimplemented XLSForm type", though the type is supported

validateSubset now reports each as an error. It takes an optional
{ fileChoices } so CSVs the caller will pass to convert() count as resolved;
`formtransform validate` passes the CSVs beside the form, as xlsform2lstsv
does. The converter throws for the same cases with messages naming the
question and what's missing, and for from_file it lists the registered
vocabularies.

The validation_relevance_survey fixture was one of these: `consent` put its
list in a non-standard `list_name` survey column, so its blessed snapshot had
an L question with no answers. It now uses `select_one yes_no`; the
re-blessed TSV/DDI gain its two answers.

Closes #41

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@jstet
jstet merged commit 5f91048 into main Sep 24, 2026
4 checks passed
@jstet
jstet deleted the fix/unresolvable-choice-lists branch September 24, 2026 16:43
@jstet jstet mentioned this pull request Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validateSubset accepts select questions whose answer options can't be resolved

1 participant