diff --git a/openapi.json b/openapi.json index 5aaf2afe..191ba110 100644 --- a/openapi.json +++ b/openapi.json @@ -38481,7 +38481,6 @@ "type": "string" }, "type": "array", - "maxItems": 200, "title": "Options" }, "fact": { @@ -51043,7 +51042,6 @@ "type": "string" }, "type": "array", - "maxItems": 200, "title": "Options" }, "fact": { @@ -54187,7 +54185,6 @@ "type": "string" }, "type": "array", - "maxItems": 200, "title": "Options" }, "hint": { diff --git a/src/api/apply/__init__.py b/src/api/apply/__init__.py index 92a10e6c..5af53646 100644 --- a/src/api/apply/__init__.py +++ b/src/api/apply/__init__.py @@ -114,6 +114,11 @@ def location(self) -> str: return " | ".join(p for p in (full, self.city, self.state) if p) +# Choices belong to the source form, not a fixed-size vocabulary. Keep the +# complete list in both request paths so a valid answer cannot be cut off. +ApplicationOptions = list[str] + + class Field_(BaseModel): """One field as the extension read it. kind is the widget: text, long, select, yesno, file, number, date. options are the select's choices @@ -124,7 +129,7 @@ class Field_(BaseModel): label: str = Field(default="", max_length=4000) kind: str = Field(default="text", max_length=20) required: bool = False - options: list[str] = Field(default_factory=list, max_length=200) + options: ApplicationOptions = Field(default_factory=list) # A config-driven reader knows which fact a selector fills (first_name, # needs_sponsorship, resume); when it says so, the label is not read. fact: str | None = Field(default=None, max_length=40) diff --git a/src/api/routers/apply.py b/src/api/routers/apply.py index 674d544a..da044080 100644 --- a/src/api/routers/apply.py +++ b/src/api/routers/apply.py @@ -628,7 +628,7 @@ class SuggestField(BaseModel): key: str = Field(min_length=1, max_length=300) label: str = Field(min_length=1, max_length=4000) kind: str = Field(default="text", max_length=20) - options: list[str] = Field(default_factory=list, max_length=200) + options: apply.ApplicationOptions = Field(default_factory=list) # The profile's answer when the options did not recognisably hold it. hint: str | None = Field(default=None, max_length=200) diff --git a/tests/test_apply_large_options.py b/tests/test_apply_large_options.py new file mode 100644 index 00000000..7ac58918 --- /dev/null +++ b/tests/test_apply_large_options.py @@ -0,0 +1,12 @@ +from api import apply +from api.routers.apply import SuggestField + + +def test_large_school_dropdown_preserves_every_choice(): + # The reported university control contained 928 choices, not 200. + options = [f"University {index}" for index in range(927)] + ["Purdue University"] + payload = dict(key="school", label="School", kind="select", options=options) + resolved = apply.Field_(**payload) + suggested = SuggestField(**payload) + assert resolved.options == suggested.options == options + assert apply.pick_option("Purdue University", resolved.options) == "Purdue University"