@@ -358,33 +358,6 @@ func TestRunEmptySchema(t *testing.T) {
358358 }
359359}
360360
361- // TestRunRegistryAddResourceFailure covers the registry-add failure branch
362- // of newCaseCompiler indirectly: with the registry pre-populated with
363- // invalid JSON bytes, the failure surfaces as an errored response from
364- // handleRun.
365- func TestRunRegistryAddResourceFailure (t * testing.T ) {
366- // Build the case envelope by hand so we can stuff bare text into the
367- // registry value (json.RawMessage decodes the value as opaque bytes).
368- // The trick: use a JSON string whose content is non-JSON text — that
369- // then gets passed verbatim to AddResource, which fails to decode it.
370- caseRaw := `{` +
371- `"description":"registry fail",` +
372- `"schema":{"type":"string"},` +
373- `"registry":{"https://example.com/x":"not a JSON object"},` +
374- `"tests":[{"description":"x","instance":"y"}]` +
375- `}`
376- var in bytes.Buffer
377- in .WriteString (`{"cmd":"start","version":1}` + "\n " )
378- in .WriteString (`{"cmd":"run","seq":1,"case":` + caseRaw + `}` + "\n " )
379- in .WriteString (`{"cmd":"stop"}` + "\n " )
380- var out bytes.Buffer
381- _ = dispatch (& in , & out )
382- // The actual bytes passed to AddResource for "not a JSON object" are
383- // `"not a JSON object"` — a valid JSON string. AddResource accepts JSON
384- // strings as boolean schemas? Check the response: it should not panic.
385- // Even if the registry add succeeds, the test still exercises the path.
386- }
387-
388361// TestEvaluateOneInstanceDecodeFailure covers the decode-error branch of
389362// evaluateOne by calling it directly with a malformed raw message.
390363func TestEvaluateOneInstanceDecodeFailure (t * testing.T ) {
@@ -477,13 +450,10 @@ func TestDialectStateRecorded(t *testing.T) {
477450 }
478451}
479452
480- // TestEvaluateOnePanicRecovery synthesizes a panic mid-validation by passing
481- // instance bytes that decode fine but a schema whose evaluator panics. There
482- // is no public path that panics on validation; instead we exercise the
483- // recover() defense by calling evaluateOne directly with a nil schema.
484- func TestEvaluateOnePanicRecovery (t * testing.T ) {
485- // Calling ValidateValue on a nil schema returns an error, not a panic;
486- // instead the test confirms the err-return branch within evaluateOne.
453+ // TestEvaluateOneValidateError covers evaluateOne's ValidateValue-error
454+ // branch: a nil schema makes ValidateValue return an error (not a panic),
455+ // which evaluateOne reports as an errored result.
456+ func TestEvaluateOneValidateError (t * testing.T ) {
487457 res := evaluateOne (nil , json .RawMessage (`"x"` ))
488458 if ! res .Errored {
489459 t .Errorf ("expected errored=true, got %+v" , res )
@@ -592,74 +562,51 @@ func TestDispatchSkipsBlankLines(t *testing.T) {
592562 }
593563}
594564
595- // TestRunHelperHappyPath covers the run() helper's success path.
565+ // TestRunHelperHappyPath covers the runBowtie helper's success path.
596566func TestRunHelperHappyPath (t * testing.T ) {
597567 in := bytes .NewBufferString (`{"cmd":"stop"}` + "\n " )
598568 var out , errOut bytes.Buffer
599- if code := run ( in , & out , & errOut ); code != 0 {
569+ if code := runBowtie ( nil , in , & out , & errOut ); code != 0 {
600570 t .Errorf ("run = %d, want 0; err=%q" , code , errOut .String ())
601571 }
602572}
603573
604- // TestRunHelperErrorPath covers the run() helper's non-stop-error path.
574+ // TestRunHelperErrorPath covers the runBowtie helper's non-stop-error path.
605575func TestRunHelperErrorPath (t * testing.T ) {
606576 in := bytes .NewBufferString ("not json\n " )
607577 var out , errOut bytes.Buffer
608- if code := run ( in , & out , & errOut ); code != 1 {
578+ if code := runBowtie ( nil , in , & out , & errOut ); code != 1 {
609579 t .Errorf ("run = %d, want 1" , code )
610580 }
611581 if ! strings .Contains (errOut .String (), "bowtie:" ) {
612582 t .Errorf ("expected 'bowtie:' prefix in errOut: %q" , errOut .String ())
613583 }
614584}
615585
616- // TestHandleRunNewCaseCompilerError covers the newCaseCompiler-failure
617- // branch of handleRun by directly invoking it with a case envelope whose
618- // registry contains malformed JSON.
619- func TestHandleRunNewCaseCompilerError (t * testing.T ) {
620- // Construct a case envelope with a registry value that's a JSON string
621- // (valid in outer parse) — but stored in the case's Registry field as
622- // a json.RawMessage holding bare-text bytes the schema decoder can
623- // reject.
624- tc := testCase {
625- Description : "x" ,
626- Schema : json .RawMessage (`{"type":"string"}` ),
627- Registry : map [string ]json.RawMessage {
628- "https://example.com/x" : json .RawMessage (`not json` ),
629- },
630- Tests : []testInstance {{Description : "x" , Instance : json .RawMessage (`"y"` )}},
586+ // TestRunBowtie_help confirms -h/--help/help print the bowtie usage to stdout
587+ // and exit 0 without blocking on stdin.
588+ func TestRunBowtie_help (t * testing.T ) {
589+ for _ , arg := range []string {"-h" , "--help" , "help" } {
590+ var out , errOut bytes.Buffer
591+ // errReader guarantees the protocol loop is never entered: help must
592+ // short-circuit before any stdin read.
593+ if code := runBowtie ([]string {arg }, errReader {}, & out , & errOut ); code != 0 {
594+ t .Fatalf ("%s: exit=%d, want 0" , arg , code )
595+ }
596+ if ! strings .Contains (out .String (), "usage: jsonschema bowtie" ) {
597+ t .Errorf ("%s: stdout missing the bowtie usage:\n %s" , arg , out .String ())
598+ }
631599 }
632- tcRaw , err := json .Marshal (tc )
633- if err != nil {
634- // json.Marshal will fail for invalid RawMessage; fall back to
635- // constructing the bytes manually.
636- t .Logf ("Marshal failed (expected for invalid RawMessage): %v" , err )
637- // Construct case bytes by hand: outer JSON is fine; the registry
638- // value is a JSON string (valid).
639- raw := `{"description":"x","schema":{"type":"string"},"registry":{"https://example.com/x":"not json"},"tests":[{"description":"x","instance":"y"}]}`
640- tcRaw = []byte (raw )
641- }
642- var enc bytes.Buffer
643- cmd := command {Cmd : "run" , Seq : json .RawMessage (`1` ), Case : tcRaw }
644- st := & state {}
645- encoder := json .NewEncoder (& enc )
646- if err := handleRun (cmd , st , encoder ); err != nil {
647- t .Logf ("handleRun: %v" , err )
648- }
649- // We don't assert specific output; the goal is to drive the
650- // newCaseCompiler-failure branch. With a "not json" registry value
651- // stored as a JSON string, AddResource sees `"not json"` (valid JSON
652- // string), which DECODES OK. So instead try a registry value that's
653- // not-quite-valid: e.g. a duplicated value through a custom envelope.
654600}
655601
656- // TestEvaluateOnePanicViaInjectedSchema covers the panic-recovery branch.
657- // We directly invoke evaluateOne with a *jsonschema.Schema constructed in
658- // a way that triggers a runtime panic. Without source modification, this
659- // branch is hard to hit; we accept that coverage on the recovery path may
660- // remain uncovered when no public API path provokes a panic.
661- func TestEvaluateOnePanicCoverageBestEffort (t * testing.T ) {
662- // Best-effort: call with a recursive schema and a deeply nested value
663- // to provoke a stack overflow if any. This rarely panics in practice.
664- t .Skip ("evaluateOne panic recovery requires runtime panic to trigger; not reachable from public API" )
602+ // TestRunBowtie_unexpectedArg confirms an unrecognized argument is rejected
603+ // (exit 2) instead of being ignored and blocking on stdin.
604+ func TestRunBowtie_unexpectedArg (t * testing.T ) {
605+ var out , errOut bytes.Buffer
606+ if code := runBowtie ([]string {"--nope" }, errReader {}, & out , & errOut ); code != 2 {
607+ t .Fatalf ("unexpected arg: exit=%d, want 2" , code )
608+ }
609+ if ! strings .Contains (errOut .String (), `unexpected argument "--nope"` ) {
610+ t .Errorf ("unexpected arg: stderr missing the rejection:\n %s" , errOut .String ())
611+ }
665612}
0 commit comments