{
  "part": 4,
  "title": "Transport verification (header/full)",
  "reset": "standard",
  "steps": [
    { "id": "4.1", "title": "header mode (the default)",
      "actions": [
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@part4a" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 3, "log": "part4a_source", "expect_exit": 0, "capture": { "name": "LOG4A" } },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "log": "part4a_target", "expect_exit": 0 }
      ],
      "assertions": [
        { "type": "regex_present", "var": "LOG4A", "pattern": "verifyTransportFile.*header check", "message": "header check mentioned in verifyTransportFile log lines" }
      ]
    },
    { "id": "4.2", "title": "full mode on an incremental send", "depends_on": ["4.1"],
      "config": { "mutations": [ { "op": "set", "path": "transport.verifyStream", "value": "full" } ], "restore": "none" },
      "actions": [
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@part4b" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 3, "log": "part4b_source", "expect_exit": 0, "capture": { "name": "LOG4B" } },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "log": "part4b_target", "expect_exit": 0 }
      ],
      "assertions": [
        { "type": "regex_present", "var": "LOG4B", "pattern": "verifyTransportFile.*header check", "message": "header check still runs" },
        { "type": "regex_present", "var": "LOG4B", "pattern": "verifyTransportFile.*full", "message": "a full-stream check also runs under verifyStream: full" }
      ]
    },
    { "id": "4.3", "title": "full mode on a brand-new dataset's first (full/base) send", "depends_on": ["4.2"],
      "config": { "mutations": [
        { "op": "insert_block", "parent": "datasets", "key": "ds3", "after": "ds2",
          "text": "ds3:\n  dataset: ds3\n  source: storage/testing/src\n  target: storage/testing/dst\n" }
      ], "restore": "none" },
      "actions": [
        { "do": "zfs_create", "dataset": "{SRC}/ds3" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds3@part4c" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 3, "log": "part4c_source", "expect_exit": 0, "capture": { "name": "LOG4C" } },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "log": "part4c_target", "expect_exit": 0 },
        { "do": "zfs_list_all", "dataset": "{DST}/ds3", "capture": { "name": "DST3_ALL", "reduce": "join" } }
      ],
      "assertions": [
        { "type": "regex_present", "var": "LOG4C", "pattern": "(?i)full/base send", "message": "log explains ds3 is a full/base send" },
        { "type": "regex_absent",  "var": "LOG4C", "pattern": "(?i)scratch dataset", "message": "no scratch-dataset mention (verifyFullSendDataset is not configured yet)" },
        { "type": "regex_absent",  "var": "LOG4C", "pattern": "FATAL ERROR", "message": "no FATAL ERROR" },
        { "type": "regex_present", "var": "DST3_ALL", "pattern": "dst/ds3\\@part4c", "message": "target received ds3's snapshot" }
      ]
    },
    { "id": "4.4", "title": "full mode on a first send, with transport.verifyFullSendDataset set", "depends_on": ["4.3"],
      "classification": "record_only",
      "_comment": "record_only per TESTING.md: 'this mechanism has not yet been confirmed against a real ZFS pool - pay close attention to the actual output ... that information is more valuable here than a clean PASS.' A failure here is exactly the finding worth capturing, not something to treat as a broken harness run.",
      "config": { "mutations": [
        { "op": "insert_block", "parent": "datasets", "key": "ds4", "after": "ds3",
          "text": "ds4:\n  dataset: ds4\n  source: storage/testing/src\n  target: storage/testing/dst\n" },
        { "op": "insert_kv", "parent": "transport", "key": "verifyFullSendDataset", "value": "storage/testing/verify", "style": "bare", "after": "verifyStream" }
      ], "restore": "none" },
      "actions": [
        { "do": "zfs_create", "dataset": "{SRC}/ds4" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds4@part4d" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 3, "log": "part4d_source", "expect_exit": 0, "capture": { "name": "LOG4D" } },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "log": "part4d_target", "expect_exit": 0 },
        { "do": "zfs_list_all", "dataset": "{DST}/ds4", "capture": { "name": "DST4_ALL", "reduce": "join" } }
      ],
      "assertions": [
        { "type": "regex_present", "var": "LOG4D", "pattern": "(?i)scratch dataset.*storage/testing/verify", "message": "ds4 is verified against the storage/testing/verify scratch dataset" },
        { "type": "dataset_has_no_children", "dataset": "{VERIFY}", "message": "the scratch dataset has no leftover children after the dry-run verification (report and DO NOT delete if this fails)" },
        { "type": "regex_absent", "var": "LOG4D", "pattern": "FATAL ERROR", "message": "no FATAL ERROR" },
        { "type": "regex_present", "var": "DST4_ALL", "pattern": "dst/ds4\\@part4d", "message": "target received ds4's snapshot" }
      ]
    },
    { "id": "4.4-cleanup", "title": "Restore Part 4's config changes so later Parts see only ds1/ds2 (TESTING.md's own 'Afterward' note)",
      "depends_on": ["4.4"], "config": { "restore": "none", "mutations": [
        { "op": "delete", "path": "transport.verifyFullSendDataset" },
        { "op": "set", "path": "transport.verifyStream", "value": "header" },
        { "op": "delete", "path": "datasets.ds4" },
        { "op": "delete", "path": "datasets.ds3" }
      ] },
      "actions": [
        { "do": "zfs_destroy", "dataset": "{SRC}/ds3", "recursive": true, "missing_ok": true },
        { "do": "zfs_destroy", "dataset": "{SRC}/ds4", "recursive": true, "missing_ok": true },
        { "do": "zfs_destroy", "dataset": "{DST}/ds3", "recursive": true, "missing_ok": true },
        { "do": "zfs_destroy", "dataset": "{DST}/ds4", "recursive": true, "missing_ok": true },
        { "do": "zfs_destroy", "dataset": "{VERIFY}", "recursive": true, "missing_ok": true }
      ]
    }
  ]
}
