# Plan: writers/template (8.5%) ## Existing - `writer_test.go`: deterministic table index values only ## Approach Pure helper functions; one test file per source file, table-driven. Then render tests through the writer. | File | Funcs | Cases | |------|-------|-------| | `filters.go` | FilterTables, FilterTablesByPattern, FilterColumns, FilterColumnsByType, FilterPrimaryKeys, FilterForeignKeys, FilterUniqueConstraints, FilterCheckConstraints, FilterNullable, FilterNotNull, matchPattern | Empty input, no match, glob patterns, nil maps | | `formatters.go` | ToJSON, ToJSONPretty, ToYAML, Indent, IndentWith, Escape, EscapeQuotes, Comment, QuoteString, UnquoteString | Empty string, multiline, special chars, marshal failure | | `loop_helpers.go` | Enumerate, Batch, Chunk, Reverse, First, Last, Skip, Take, Concat, Unique, SortBy, GroupBy, CountIf, getFieldValue, compareValues | Empty, n > len, n <= 0, non-slice input, missing field | | `safe_access.go` | Get, GetOr, GetPath, GetPathOr, SafeIndex, SafeIndexOr, Has, HasPath, Keys, Merge, Pick, Omit, SliceContains, IndexOf, Pluck | nil, missing key, nested path, out-of-range | | `string_helpers.go` | ToUpper, ToLower, ToCamelCase, and rest | Empty, snake/kebab/space input, unicode | | `errors.go` | Error, Unwrap, NewTemplate{Load,Parse,Execute}Error | errors.Is/As, message contents | | `funcmap.go` | BuildFuncMap | Every registered name resolves and is callable | | `type_mappers.go`, `template_data.go` | check after above | | | `writer.go` | WriteDatabase/Schema/Table, modes | Template load/parse/execute error paths; per-table, per-schema, whole-db modes; output to file vs stdout | ## Data needed - 2-3 small template fixtures in `pkg/writers/template/testdata/` (valid, parse error, execute error). - Schema input: reuse `tests/assets/dbml/simple.dbml` / `complex.dbml`.