/**
 * behavior-generator.ts — postcondition emission tests (#53D).
 *
 * The generator emits one `console.assert(true, '<text>');` line per
 * postcondition string in the dev-mode block. Pre-#53D the wrapper was
 * single-quoted; postcondition text containing apostrophes — common in
 * natural prose like "no other nodes' cache entries are affected" —
 * terminated the JS string and esbuild rejected the file.
 *
 * #53D fix: emit with backticks + escape any backticks / `${` markers
 * in the source. These tests pin the new shape so a future change
 * can't silently regress to single-quoted strings.
 */

import { describe, it, expect } from 'vitest';
import { generatePostconditionVerification } from '../behavior-generator.js';

describe('generatePostconditionVerification — 53D apostrophe handling', () => {
  it('returns empty string for empty input', () => {
    expect(generatePostconditionVerification([])).toBe('');
  });

  it('emits backtick-wrapped strings (not single-quoted)', () => {
    const out = generatePostconditionVerification(['simple postcondition']);
    expect(out).toContain('`POSTCONDITION: simple postcondition`');
    expect(out).not.toContain("'POSTCONDITION:");
  });

  it('preserves apostrophes verbatim in postcondition text', () => {
    const out = generatePostconditionVerification([
      "no other nodes' cache entries are affected",
    ]);
    expect(out).toContain("nodes' cache entries");
    // Must not have escaped the apostrophe — backticks accept it natively.
    expect(out).not.toContain("nodes\\'");
  });

  it('escapes embedded backticks to prevent string termination', () => {
    const out = generatePostconditionVerification([
      'value matches `expected` shape',
    ]);
    expect(out).toContain('value matches \\`expected\\` shape');
  });

  it('escapes ${...} interpolation markers to prevent template execution', () => {
    const out = generatePostconditionVerification([
      'rejects ${malicious} payloads',
    ]);
    expect(out).toContain('rejects \\${malicious} payloads');
    // The marker must NOT appear unescaped — that would let the spec
    // author smuggle template-literal interpolation into realized code.
    expect(out).not.toMatch(/[^\\]\$\{malicious\}/);
  });

  it('escapes backslashes before other escapes (stable round-trip)', () => {
    const out = generatePostconditionVerification(['regex \\d+ matches']);
    expect(out).toContain('regex \\\\d+ matches');
  });

  it('emits one console.assert line per postcondition', () => {
    const out = generatePostconditionVerification(['p1', 'p2', 'p3']);
    const matches = out.match(/console\.assert\(true,/g);
    expect(matches?.length).toBe(3);
  });

  it('wraps the assertion block in NODE_ENV development guard', () => {
    const out = generatePostconditionVerification(['p1']);
    expect(out).toContain("process.env.NODE_ENV === 'development'");
    expect(out).toContain('// === POSTCONDITIONS (dev-mode) ===');
  });

  it('produces output that parses as valid JavaScript', async () => {
    // The whole point of #53D — the emitted code must actually parse.
    // Use Function() as a lightweight syntax-check (rejects malformed
    // template literals just as esbuild would).
    const realistic = generatePostconditionVerification([
      "no other nodes' cache entries are affected",
      'response includes a `Last-Modified` header',
      'rejects ${oversized} payloads with 413',
    ]);
    // Wrap in a function body to give it a syntactic context.
    const wrap = `(function() {\n${realistic}\n})`;
    expect(() => new Function(`return ${wrap}`)).not.toThrow();
  });
});
