@@ -149,7 +149,7 @@ describe('proposal-validator', () => {
149149 expect ( result . valid ) . toBe ( true ) ;
150150 } ) ;
151151
152- it ( 'blocks profanity' , ( ) => {
152+ it ( 'sanitizes profanity in choice text ' , ( ) => {
153153 const proposal = {
154154 choice_text : 'A damn good choice' ,
155155 content : {
@@ -159,11 +159,11 @@ describe('proposal-validator', () => {
159159 } ;
160160
161161 const result = ProposalValidator . quickValidate ( proposal ) ;
162- expect ( result . valid ) . toBe ( false ) ;
163- expect ( result . reason ) . toContain ( 'safety filter ' ) ;
162+ expect ( result . valid ) . toBe ( true ) ;
163+ expect ( result . sanitizedProposal . choice_text ) . toContain ( '[expletive] ' ) ;
164164 } ) ;
165165
166- it ( 'blocks profanity in content' , ( ) => {
166+ it ( 'sanitizes profanity in content' , ( ) => {
167167 const proposal = {
168168 choice_text : 'Clean choice' ,
169169 content : {
@@ -173,6 +173,34 @@ describe('proposal-validator', () => {
173173 } ;
174174
175175 const result = ProposalValidator . quickValidate ( proposal ) ;
176+ expect ( result . valid ) . toBe ( true ) ;
177+ expect ( result . sanitizedProposal . content . text ) . toContain ( '[expletive]' ) ;
178+ } ) ;
179+
180+ it ( 'fails on explicit content' , ( ) => {
181+ const proposal = {
182+ choice_text : 'Look closer' ,
183+ content : {
184+ text : 'The chamber was a torture chamber filled with gore.' ,
185+ return_path : 'campfire'
186+ }
187+ } ;
188+
189+ const result = ProposalValidator . quickValidate ( proposal ) ;
190+ expect ( result . valid ) . toBe ( false ) ;
191+ expect ( result . reason ) . toContain ( 'Explicit content' ) ;
192+ } ) ;
193+
194+ it ( 'fails on ending return path' , ( ) => {
195+ const proposal = {
196+ choice_text : 'End it' ,
197+ content : {
198+ text : 'The story ends here.' ,
199+ return_path : 'rescue_end'
200+ }
201+ } ;
202+
203+ const result = ProposalValidator . quickValidate ( proposal , { validReturnPaths : [ ] } ) ;
176204 expect ( result . valid ) . toBe ( false ) ;
177205 } ) ;
178206 } ) ;
@@ -198,6 +226,31 @@ describe('proposal-validator', () => {
198226
199227 expect ( result . valid ) . toBe ( true ) ;
200228 expect ( result . errors ) . toHaveLength ( 0 ) ;
229+ expect ( result . report ) . toBeTruthy ( ) ;
230+ } ) ;
231+
232+ it ( 'reports sanitization transforms' , ( ) => {
233+ const proposal = {
234+ choice_text : 'A damn good choice' ,
235+ content : {
236+ branch_type : 'narrative_delta' ,
237+ text : 'Line 1\n\n\nLine 2 with <b>markup</b>.' ,
238+ return_path : 'campfire'
239+ } ,
240+ metadata : {
241+ confidence_score : 0.7
242+ }
243+ } ;
244+
245+ const result = ProposalValidator . validateProposal ( proposal , {
246+ validReturnPaths : [ 'campfire' ]
247+ } ) ;
248+
249+ expect ( result . valid ) . toBe ( true ) ;
250+ expect ( result . report . status ) . toBe ( 'rejected_with_sanitization' ) ;
251+ expect ( result . report . rules . some ( rule => rule . result === 'sanitized' ) ) . toBe ( true ) ;
252+ expect ( result . sanitizedProposal . content . text ) . not . toContain ( '<b>' ) ;
253+ expect ( result . sanitizedProposal . choice_text ) . toContain ( '[expletive]' ) ;
201254 } ) ;
202255 } ) ;
203256} ) ;
0 commit comments