dre-protocol 0.11.1

The DRE plugin protocol: frames, messages, host side, plugin SDK and conformance suite
Documentation
{
  "description": "DRE plugin protocol conformance cases. Each case is a request sequence and the properties its replies must have; a runner drives a plugin over stdin and stdout and checks them. The format is described in docs/protocol.md (Conformance).",
  "cases": [
    {
      "name": "asking for a plugin the package doesn't provide gets an error reply",
      "scope": "package",
      "steps": [
        {"hello": {"plugin": "format/dre_conformance_unknown"}, "expect": {"type": "error"}}
      ]
    },
    {
      "name": "handshake negotiates a supported version and reports its identity",
      "steps": [
        {
          "hello": {},
          "expect": {
            "type": "hello",
            "id": {"$absent": true},
            "protocol_version": "${max_version}",
            "kind": "${kind}",
            "name": "${name}",
            "version": {"$nonempty": true},
            "capabilities": {"$every": {"$in": "${capabilities}"}},
            "provides": {"$one_of": [{"$absent": true}, {"$contains": "${plugin}"}]}
          }
        }
      ]
    },
    {
      "name": "an unsupported protocol range is refused, not hung on",
      "steps": [
        {
          "hello": {"min_version": "${far_version}", "max_version": "${far_version}"},
          "expect": {"type": "version_mismatch", "max_version": {"$lt": "${far_version}"}}
        }
      ]
    },
    {
      "name": "protocol 0 is still spoken, for an older core, without request ids",
      "steps": [
        {"hello": {"min_version": 0, "max_version": 0}, "expect": {"type": "hello", "protocol_version": 0}},
        {"send": {"type": "describe"}, "expect": {"type": "describe", "id": {"$absent": true}}}
      ]
    },
    {
      "name": "replies carry the request's id, and a cancel for a request that isn't running is ignored",
      "steps": [
        {"send": {"type": "cancel", "id": 999}},
        {"send": {"type": "describe", "id": 41}, "expect": {"type": "describe", "id": 41}},
        {"send": {"type": "frobnicate", "id": 42}, "expect": {"type": "error", "id": 42}}
      ]
    },
    {
      "name": "describe is answered",
      "steps": [
        {"send": {"type": "describe", "id": 1}, "expect": {"type": "describe", "id": 1, "connection_fields": {"$type": "array"}}}
      ]
    },
    {
      "name": "describe gives the identifier quote character",
      "kinds": ["source"],
      "steps": [
        {"send": {"type": "describe", "id": 1}, "expect": {"type": "describe", "id": 1, "identifier_quote": {"$len": 1}}}
      ]
    },
    {
      "name": "validate is advertised, answered, and refuses an option the plugin doesn't declare",
      "steps": [
        {"hello": {}, "expect": {"type": "hello", "capabilities": {"$contains": "validate"}}},
        {"send": {"type": "validate", "id": 1, "options": {}}, "expect": {"type": "validated", "id": 1, "errors": {"$type": "array"}}},
        {
          "send": {"type": "validate", "id": 2, "options": {"dre_conformance_unknown_option": true}},
          "expect": {"type": "validated", "id": 2, "errors": {"$contains": {"$contains": "dre_conformance_unknown_option"}}}
        }
      ]
    },
    {
      "name": "validate_connection is answered without connecting, and an unknown key only warns",
      "kinds": ["source", "destination"],
      "steps": [
        {
          "send": {"type": "validate_connection", "id": 1, "connection": {"dre_conformance_unknown_key": true}},
          "expect": {"type": "validated", "id": 1, "warnings": {"$contains": {"$contains": "dre_conformance_unknown_key"}}}
        }
      ]
    },
    {
      "name": "an unknown request gets an error reply with a known kind, and the plugin keeps serving",
      "steps": [
        {
          "send": {"type": "frobnicate", "id": 1},
          "expect": {
            "type": "error",
            "id": 1,
            "message": {"$nonempty": true},
            "kind": {"$one_of": [{"$absent": true}, {"$in": "${error_kinds}"}]},
            "code": {"$one_of": [{"$absent": true}, {"$contains": "/"}]}
          }
        },
        {"send": {"type": "describe", "id": 2}, "expect": {"type": "describe", "id": 2}}
      ]
    },
    {
      "name": "a request meant for another plugin kind gets an error reply",
      "kinds": ["source"],
      "steps": [
        {"send": {"type": "finish", "id": 1}, "expect": {"type": "error", "id": 1}}
      ]
    },
    {
      "name": "a request meant for another plugin kind gets an error reply",
      "kinds": ["format", "destination"],
      "steps": [
        {"send": {"type": "check", "id": 1, "sql": "select 1"}, "expect": {"type": "error", "id": 1}}
      ]
    },
    {
      "name": "a malformed frame is reported or ends the plugin, never hangs",
      "steps": [
        {"send_raw": "000000025821", "expect": {"type": "error"}, "or_exit": true}
      ]
    },
    {
      "name": "a write of an empty result set makes a valid file (or asks for options)",
      "kinds": ["format"],
      "steps": [
        {
          "send": {
            "type": "write",
            "id": 1,
            "path": "${tmp}/empty.${name}",
            "format": "${name}",
            "options": {},
            "result_sets": [{"name": "empty", "query": "empty", "result_index": 1}]
          }
        },
        {"send_arrow": {"columns": [{"name": "id", "type": "int64"}, {"name": "name", "type": "utf8"}], "rows": 0}},
        {"send": {"type": "result_set_end", "id": 1}},
        {
          "send": {"type": "finish", "id": 1},
          "expect": {
            "$one_of": [
              {"type": "written", "id": 1, "files": {"$nonempty": true, "$every": {"$file_exists": true}}},
              {"type": "error", "id": 1, "kind": "config"}
            ]
          }
        }
      ]
    },
    {
      "name": "a write of a result set with rows, across several Arrow frames, makes a valid file (or asks for options)",
      "kinds": ["format"],
      "steps": [
        {
          "send": {
            "type": "write",
            "id": 1,
            "path": "${tmp}/rows.${name}",
            "format": "${name}",
            "options": {},
            "result_sets": [{"name": "rows", "query": "rows", "result_index": 1}]
          }
        },
        {
          "send_arrow": {
            "rows": 3,
            "columns": [
              {"name": "id", "type": "int64", "values": [1, 2, null]},
              {"name": "name", "type": "utf8", "values": ["a", "b, \"quoted\"", null]},
              {"name": "amount", "type": "float64", "values": [1.5, -2.25, null]},
              {"name": "ok", "type": "boolean", "values": [true, false, null]}
            ]
          }
        },
        {
          "send_arrow": {
            "rows": 2,
            "columns": [
              {"name": "id", "type": "int64", "values": [4, 5]},
              {"name": "name", "type": "utf8", "values": ["d", "é"]},
              {"name": "amount", "type": "float64", "values": [0, 1000000]},
              {"name": "ok", "type": "boolean", "values": [true, true]}
            ]
          }
        },
        {"send": {"type": "result_set_end", "id": 1}},
        {
          "send": {"type": "finish", "id": 1},
          "expect": {
            "$one_of": [
              {"type": "written", "id": 1, "files": {"$nonempty": true, "$every": {"$file_exists": true}}},
              {"type": "error", "id": 1, "kind": "config"}
            ]
          }
        }
      ]
    },
    {
      "name": "a source opens a session, streams a result set and honours row_limit",
      "kinds": ["source"],
      "session": ["rows", "empty"],
      "steps": [
        {"send": {"type": "open", "id": 1, "connection": "${connection}", "read_only": false}, "expect": {"type": "ok", "id": 1}},
        {"send": {"type": "execute", "id": 2, "sql": "${sql.rows}"}, "expect": {"type": "result", "id": 2, "columns": {"$nonempty": true}}},
        {"result_set": {"id": 2, "rows": {"$min": 3}}},
        {"send": {"type": "execute", "id": 3, "sql": "${sql.rows}", "row_limit": 2}, "expect": {"type": "result", "id": 3}},
        {"result_set": {"id": 3, "rows": 2}},
        {"send": {"type": "execute", "id": 4, "sql": "${sql.empty}"}, "expect": {"type": "result", "id": 4}},
        {"result_set": {"id": 4, "rows": 0}}
      ]
    },
    {
      "name": "a statement without a result set replies no_result",
      "kinds": ["source"],
      "session": ["no_result"],
      "steps": [
        {"send": {"type": "open", "id": 1, "connection": "${connection}", "read_only": false}, "expect": {"type": "ok", "id": 1}},
        {"send": {"type": "execute", "id": 2, "sql": "${sql.no_result}"}, "expect": {"type": "no_result", "id": 2}}
      ]
    },
    {
      "name": "check verifies a statement without running it",
      "kinds": ["source"],
      "requires": ["check"],
      "session": ["rows"],
      "steps": [
        {"send": {"type": "open", "id": 1, "connection": "${connection}", "read_only": false}, "expect": {"type": "ok", "id": 1}},
        {"send": {"type": "check", "id": 2, "sql": "${sql.rows}"}, "expect": {"type": "ok", "id": 2}}
      ]
    },
    {
      "name": "a failed statement gets an error reply and the session keeps serving",
      "kinds": ["source"],
      "session": ["rows", "error"],
      "steps": [
        {"send": {"type": "open", "id": 1, "connection": "${connection}", "read_only": false}, "expect": {"type": "ok", "id": 1}},
        {
          "send": {"type": "execute", "id": 2, "sql": "${sql.error}"},
          "expect": {"type": "error", "id": 2, "message": {"$nonempty": true}, "kind": {"$one_of": [{"$absent": true}, {"$in": "${error_kinds}"}]}}
        },
        {"send": {"type": "execute", "id": 3, "sql": "${sql.rows}"}, "expect": {"type": "result", "id": 3}},
        {"result_set": {"id": 3, "rows": {"$min": 1}}}
      ]
    },
    {
      "name": "a cancel stops a running statement, which replies cancelled, and the plugin keeps serving",
      "kinds": ["source"],
      "session": ["slow"],
      "steps": [
        {"send": {"type": "open", "id": 1, "connection": "${connection}", "read_only": false}, "expect": {"type": "ok", "id": 1}},
        {"send": {"type": "execute", "id": 2, "sql": "${sql.slow}"}},
        {"send": {"type": "cancel", "id": 2}, "expect": {"type": "error", "id": 2, "kind": "cancelled"}},
        {"send": {"type": "describe", "id": 3}, "expect": {"type": "describe", "id": 3}}
      ]
    },
    {
      "name": "deliver with options gets a reply and the plugin keeps serving",
      "kinds": ["destination"],
      "steps": [
        {
          "send": {
            "type": "deliver",
            "id": 1,
            "local_path": "/nonexistent/dre-conformance/a.csv",
            "remote_path": "dre-conformance/a.csv",
            "connection": {},
            "options": {"conformance": true}
          },
          "expect": {"$one_of": [{"type": "delivered", "id": 1}, {"type": "error", "id": 1}]}
        },
        {"send": {"type": "describe", "id": 2}, "expect": {"type": "describe", "id": 2}}
      ]
    },
    {
      "name": "a multi-file deliver gets a reply and the plugin keeps serving",
      "kinds": ["destination"],
      "requires": ["multi_file"],
      "steps": [
        {
          "send": {
            "type": "deliver",
            "id": 1,
            "files": [
              {"local_path": "/nonexistent/dre-conformance/a.csv", "remote_path": "dre-conformance/a.csv"},
              {"local_path": "/nonexistent/dre-conformance/b.csv", "remote_path": "dre-conformance/b.csv"}
            ],
            "connection": {},
            "options": {}
          },
          "expect": {"$one_of": [{"type": "delivered", "id": 1}, {"type": "error", "id": 1}]}
        },
        {"send": {"type": "describe", "id": 2}, "expect": {"type": "describe", "id": 2}}
      ]
    },
    {
      "name": "a message deliver gets a reply and the plugin keeps serving",
      "kinds": ["destination"],
      "requires": ["message"],
      "steps": [
        {
          "send": {
            "type": "deliver",
            "id": 1,
            "connection": {},
            "options": {},
            "message": {"title": "Conformance", "text": "**DRE** conformance check", "path": "/nonexistent/dre-conformance/message.md"}
          },
          "expect": {"$one_of": [{"type": "delivered", "id": 1}, {"type": "error", "id": 1}]}
        },
        {"send": {"type": "describe", "id": 2}, "expect": {"type": "describe", "id": 2}}
      ]
    },
    {
      "name": "a message_only plugin also advertises message",
      "kinds": ["destination"],
      "requires": ["message_only"],
      "steps": [
        {"hello": {}, "expect": {"type": "hello", "capabilities": {"$contains": "message"}}}
      ]
    },
    {
      "name": "a message_only plugin refuses a file deliver and keeps serving",
      "kinds": ["destination"],
      "requires": ["message_only"],
      "steps": [
        {
          "send": {
            "type": "deliver",
            "id": 1,
            "local_path": "/nonexistent/dre-conformance/a.csv",
            "remote_path": "dre-conformance/a.csv",
            "connection": {},
            "options": {}
          },
          "expect": {"type": "error", "id": 1}
        },
        {"send": {"type": "describe", "id": 2}, "expect": {"type": "describe", "id": 2}}
      ]
    },
    {
      "name": "close ends the process with exit code 0",
      "steps": [
        {"send": {"type": "close", "id": 1}},
        {"exit": 0}
      ]
    },
    {
      "name": "end of input ends the process",
      "steps": [
        {"close_input": true},
        {"exit": {"$any": true}}
      ]
    }
  ]
}