flow.record 3.21.dev2__tar.gz → 3.21.dev3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/PKG-INFO +1 -1
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/csvfile.py +8 -3
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/elastic.py +6 -5
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/jsonfile.py +2 -2
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/splunk.py +2 -2
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/tools/rdump.py +17 -3
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/utils.py +29 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/version.py +2 -2
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow.record.egg-info/PKG-INFO +1 -1
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow.record.egg-info/SOURCES.txt +1 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_record_adapter.py +1 -1
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_regression.py +20 -0
- flow_record-3.21.dev3/tests/test_utils.py +25 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/.git-blame-ignore-revs +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/COPYRIGHT +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/LICENSE +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/MANIFEST.in +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/README.md +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/examples/filesystem.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/examples/passivedns.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/examples/records.json +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/examples/tcpconn.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/__init__.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/__init__.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/archive.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/avro.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/broker.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/duckdb.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/line.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/mongo.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/split.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/sqlite.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/stream.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/text.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/adapter/xlsx.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/base.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/exceptions.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/fieldtypes/__init__.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/fieldtypes/credential.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/fieldtypes/net/__init__.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/fieldtypes/net/ip.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/fieldtypes/net/ipv4.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/fieldtypes/net/tcp.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/fieldtypes/net/udp.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/jsonpacker.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/packer.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/selector.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/stream.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/tools/__init__.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/tools/geoip.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow/record/whitelist.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow.record.egg-info/dependency_links.txt +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow.record.egg-info/entry_points.txt +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow.record.egg-info/requires.txt +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/flow.record.egg-info/top_level.txt +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/pyproject.toml +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/setup.cfg +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/__init__.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/_utils.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/docs/Makefile +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/docs/conf.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/docs/index.rst +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/selector_explain_example.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/standalone_test.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_adapter_line.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_adapter_text.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_avro.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_avro_adapter.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_compiled_selector.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_csv_adapter.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_deprecations.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_elastic_adapter.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_fieldtype_ip.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_fieldtypes.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_json_packer.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_json_record_adapter.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_multi_timestamp.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_packer.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_rdump.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_record.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_record_descriptor.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_selector.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_splunk_adapter.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_sqlite_duckdb_adapter.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tests/test_xlsx_adapter.py +0 -0
- {flow_record-3.21.dev2 → flow_record-3.21.dev3}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.21.
|
|
3
|
+
Version: 3.21.dev3
|
|
4
4
|
Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -9,7 +9,7 @@ from flow.record import RecordDescriptor
|
|
|
9
9
|
from flow.record.adapter import AbstractReader, AbstractWriter
|
|
10
10
|
from flow.record.base import Record, normalize_fieldname
|
|
11
11
|
from flow.record.selector import make_selector
|
|
12
|
-
from flow.record.utils import is_stdout
|
|
12
|
+
from flow.record.utils import boolean_argument, is_stdout
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
15
|
from collections.abc import Iterator
|
|
@@ -17,11 +17,12 @@ if TYPE_CHECKING:
|
|
|
17
17
|
__usage__ = """
|
|
18
18
|
Comma-separated values (CSV) adapter
|
|
19
19
|
---
|
|
20
|
-
Write usage: rdump -w csvfile://[PATH]?lineterminator=[TERMINATOR]
|
|
20
|
+
Write usage: rdump -w csvfile://[PATH]?lineterminator=[TERMINATOR]&header=[HEADER]
|
|
21
21
|
Read usage: rdump csvfile://[PATH]?fields=[FIELDS]
|
|
22
22
|
[PATH]: path to file. Leave empty or "-" to output to stdout
|
|
23
23
|
|
|
24
24
|
Optional parameters:
|
|
25
|
+
[HEADER]: if set to false, it will not print the CSV header (default: true)
|
|
25
26
|
[TERMINATOR]: line terminator, default is \\r\\n
|
|
26
27
|
[FIELDS]: comma-separated list of CSV fields (in case of missing CSV header)
|
|
27
28
|
"""
|
|
@@ -34,6 +35,7 @@ class CsvfileWriter(AbstractWriter):
|
|
|
34
35
|
fields: str | list[str] | None = None,
|
|
35
36
|
exclude: str | list[str] | None = None,
|
|
36
37
|
lineterminator: str = "\r\n",
|
|
38
|
+
header: str = "true",
|
|
37
39
|
**kwargs,
|
|
38
40
|
):
|
|
39
41
|
self.fp = None
|
|
@@ -52,13 +54,16 @@ class CsvfileWriter(AbstractWriter):
|
|
|
52
54
|
self.fields = self.fields.split(",")
|
|
53
55
|
if isinstance(self.exclude, str):
|
|
54
56
|
self.exclude = self.exclude.split(",")
|
|
57
|
+
self.header = boolean_argument(header)
|
|
55
58
|
|
|
56
59
|
def write(self, r: Record) -> None:
|
|
57
60
|
rdict = r._asdict(fields=self.fields, exclude=self.exclude)
|
|
58
61
|
if not self.desc or self.desc != r._desc:
|
|
59
62
|
self.desc = r._desc
|
|
60
63
|
self.writer = csv.DictWriter(self.fp, rdict, lineterminator=self.lineterminator)
|
|
61
|
-
self.
|
|
64
|
+
if self.header:
|
|
65
|
+
# Write header only if it is requested
|
|
66
|
+
self.writer.writeheader()
|
|
62
67
|
self.writer.writerow(rdict)
|
|
63
68
|
|
|
64
69
|
def flush(self) -> None:
|
|
@@ -19,6 +19,7 @@ from flow.record.adapter import AbstractReader, AbstractWriter
|
|
|
19
19
|
from flow.record.base import Record, RecordDescriptor
|
|
20
20
|
from flow.record.fieldtypes import fieldtype_for_value
|
|
21
21
|
from flow.record.jsonpacker import JsonRecordPacker
|
|
22
|
+
from flow.record.utils import boolean_argument
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
24
25
|
from collections.abc import Iterator
|
|
@@ -72,9 +73,9 @@ class ElasticWriter(AbstractWriter):
|
|
|
72
73
|
|
|
73
74
|
self.index = index
|
|
74
75
|
self.uri = uri
|
|
75
|
-
verify_certs =
|
|
76
|
-
http_compress =
|
|
77
|
-
self.hash_record =
|
|
76
|
+
verify_certs = boolean_argument(verify_certs)
|
|
77
|
+
http_compress = boolean_argument(http_compress)
|
|
78
|
+
self.hash_record = boolean_argument(hash_record)
|
|
78
79
|
queue_size = int(queue_size)
|
|
79
80
|
|
|
80
81
|
if not uri.lower().startswith(("http://", "https://")):
|
|
@@ -216,8 +217,8 @@ class ElasticReader(AbstractReader):
|
|
|
216
217
|
self.index = index
|
|
217
218
|
self.uri = uri
|
|
218
219
|
self.selector = selector
|
|
219
|
-
verify_certs =
|
|
220
|
-
http_compress =
|
|
220
|
+
verify_certs = boolean_argument(verify_certs)
|
|
221
|
+
http_compress = boolean_argument(http_compress)
|
|
221
222
|
|
|
222
223
|
if not uri.lower().startswith(("http://", "https://")):
|
|
223
224
|
uri = "http://" + uri
|
|
@@ -8,7 +8,7 @@ from flow.record import JsonRecordPacker
|
|
|
8
8
|
from flow.record.adapter import AbstractReader, AbstractWriter
|
|
9
9
|
from flow.record.fieldtypes import fieldtype_for_value
|
|
10
10
|
from flow.record.selector import make_selector
|
|
11
|
-
from flow.record.utils import is_stdout
|
|
11
|
+
from flow.record.utils import boolean_argument, is_stdout
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from collections.abc import Iterator
|
|
@@ -33,7 +33,7 @@ class JsonfileWriter(AbstractWriter):
|
|
|
33
33
|
def __init__(
|
|
34
34
|
self, path: str | Path | BinaryIO, indent: str | int | None = None, descriptors: bool = True, **kwargs
|
|
35
35
|
):
|
|
36
|
-
self.descriptors =
|
|
36
|
+
self.descriptors = boolean_argument(descriptors)
|
|
37
37
|
self.fp = record.open_path_or_stream(path, "w")
|
|
38
38
|
if isinstance(indent, str):
|
|
39
39
|
indent = int(indent)
|
|
@@ -18,7 +18,7 @@ except ImportError:
|
|
|
18
18
|
|
|
19
19
|
from flow.record.adapter import AbstractReader, AbstractWriter
|
|
20
20
|
from flow.record.jsonpacker import JsonRecordPacker
|
|
21
|
-
from flow.record.utils import to_base64, to_bytes, to_str
|
|
21
|
+
from flow.record.utils import boolean_argument, to_base64, to_bytes, to_str
|
|
22
22
|
|
|
23
23
|
if TYPE_CHECKING:
|
|
24
24
|
from flow.record.base import Record
|
|
@@ -218,7 +218,7 @@ class SplunkWriter(AbstractWriter):
|
|
|
218
218
|
self.token = f"Splunk {self.token}"
|
|
219
219
|
|
|
220
220
|
# Assume verify=True unless specified otherwise.
|
|
221
|
-
self.verify =
|
|
221
|
+
self.verify = boolean_argument(ssl_verify)
|
|
222
222
|
if not self.verify:
|
|
223
223
|
log.warning("Certificate verification is disabled")
|
|
224
224
|
|
|
@@ -107,7 +107,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
107
107
|
output.add_argument("--skip", metavar="COUNT", type=int, default=0, help="Skip the first COUNT records")
|
|
108
108
|
output.add_argument("-w", "--writer", metavar="OUTPUT", default=None, help="Write records to output")
|
|
109
109
|
output.add_argument(
|
|
110
|
-
"-m",
|
|
110
|
+
"-m",
|
|
111
|
+
"--mode",
|
|
112
|
+
default=None,
|
|
113
|
+
choices=("csv", "csv-no-header", "json", "jsonlines", "line", "line-verbose"),
|
|
114
|
+
help="Output mode",
|
|
111
115
|
)
|
|
112
116
|
output.add_argument(
|
|
113
117
|
"--split", metavar="COUNT", default=None, type=int, help="Write record files smaller than COUNT records"
|
|
@@ -180,6 +184,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
180
184
|
default=argparse.SUPPRESS,
|
|
181
185
|
help="Short for --mode=line-verbose",
|
|
182
186
|
)
|
|
187
|
+
aliases.add_argument(
|
|
188
|
+
"-Cn",
|
|
189
|
+
"--csv-no-header",
|
|
190
|
+
action="store_const",
|
|
191
|
+
const="csv-no-header",
|
|
192
|
+
dest="mode",
|
|
193
|
+
default=argparse.SUPPRESS,
|
|
194
|
+
help="Short for --mode=csv-no-header",
|
|
195
|
+
)
|
|
183
196
|
|
|
184
197
|
args = parser.parse_args(argv)
|
|
185
198
|
|
|
@@ -198,6 +211,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
198
211
|
if not args.writer:
|
|
199
212
|
mode_to_uri = {
|
|
200
213
|
"csv": "csvfile://",
|
|
214
|
+
"csv-no-header": "csvfile://?header=false",
|
|
201
215
|
"json": "jsonfile://?indent=2&descriptors=false",
|
|
202
216
|
"jsonlines": "jsonfile://?descriptors=false",
|
|
203
217
|
"line": "line://",
|
|
@@ -210,7 +224,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
210
224
|
"format_spec": args.format,
|
|
211
225
|
}
|
|
212
226
|
query = urlencode({k: v for k, v in qparams.items() if v})
|
|
213
|
-
uri += "&" if urlparse(uri).query else "?"
|
|
227
|
+
uri += f"&{query}" if urlparse(uri).query else f"?{query}"
|
|
214
228
|
|
|
215
229
|
if args.split:
|
|
216
230
|
if not args.writer:
|
|
@@ -221,7 +235,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
221
235
|
query_dict = dict(parse_qsl(parsed.query))
|
|
222
236
|
query_dict.update({"count": args.split, "suffix-length": args.suffix_length})
|
|
223
237
|
query = urlencode(query_dict)
|
|
224
|
-
uri = parsed.scheme
|
|
238
|
+
uri = f"{parsed.scheme}://{parsed.netloc}{parsed.path}?{query}"
|
|
225
239
|
|
|
226
240
|
record_field_rewriter = None
|
|
227
241
|
if fields or fields_to_exclude or args.exec_expression:
|
|
@@ -117,3 +117,32 @@ class EventHandler:
|
|
|
117
117
|
def __call__(self, *args, **kwargs) -> None:
|
|
118
118
|
for h in self.handlers:
|
|
119
119
|
h(*args, **kwargs)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def boolean_argument(value: str | bool | int) -> bool:
|
|
123
|
+
"""Convert a string, boolean, or integer to a boolean value.
|
|
124
|
+
|
|
125
|
+
This function interprets various string representations of boolean values,
|
|
126
|
+
such as "true", "false", "1", "0", "yes", "no".
|
|
127
|
+
It also accepts boolean and integer values directly.
|
|
128
|
+
|
|
129
|
+
Arguments:
|
|
130
|
+
value: The value to convert. Can be a string, boolean, or integer.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
bool: The converted boolean value.
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
ValueError: If the value cannot be interpreted as a boolean.
|
|
137
|
+
"""
|
|
138
|
+
if isinstance(value, bool):
|
|
139
|
+
return value
|
|
140
|
+
if isinstance(value, int):
|
|
141
|
+
return bool(value)
|
|
142
|
+
if isinstance(value, str):
|
|
143
|
+
value = value.lower()
|
|
144
|
+
if value in ("true", "1", "y", "yes", "on"):
|
|
145
|
+
return True
|
|
146
|
+
if value in ("false", "0", "n", "no", "off"):
|
|
147
|
+
return False
|
|
148
|
+
raise ValueError(f"Invalid boolean argument: {value}")
|
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '3.21.
|
|
21
|
-
__version_tuple__ = version_tuple = (3, 21, '
|
|
20
|
+
__version__ = version = '3.21.dev3'
|
|
21
|
+
__version_tuple__ = version_tuple = (3, 21, 'dev3')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.21.
|
|
3
|
+
Version: 3.21.dev3
|
|
4
4
|
Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -242,7 +242,7 @@ def test_record_adapter_archive(tmp_path: Path) -> None:
|
|
|
242
242
|
|
|
243
243
|
# defaults to always archive by /YEAR/MONTH/DAY/ dir structure
|
|
244
244
|
outdir = tmp_path.joinpath(f"{dt:%Y/%m/%d}")
|
|
245
|
-
assert
|
|
245
|
+
assert list(outdir.iterdir())
|
|
246
246
|
|
|
247
247
|
# read the archived records and test filename and counts
|
|
248
248
|
count2 = 0
|
|
@@ -8,6 +8,7 @@ import subprocess
|
|
|
8
8
|
import sys
|
|
9
9
|
from datetime import datetime, timezone
|
|
10
10
|
from io import BytesIO
|
|
11
|
+
from pathlib import Path
|
|
11
12
|
from typing import Callable
|
|
12
13
|
from unittest.mock import MagicMock, patch
|
|
13
14
|
|
|
@@ -691,5 +692,24 @@ def test_record_writer_default_stdout(capsysbinary: pytest.CaptureFixture) -> No
|
|
|
691
692
|
assert stdout.startswith(b"\x00\x00\x00\x0f\xc4\rRECORDSTREAM\n")
|
|
692
693
|
|
|
693
694
|
|
|
695
|
+
def test_rdump_selected_fields(capsysbinary: pytest.CaptureFixture) -> None:
|
|
696
|
+
"""Test rdump regression where selected fields was not propagated properly to adapter."""
|
|
697
|
+
|
|
698
|
+
# Pastebin record used for this test
|
|
699
|
+
example_records_json_path = Path(__file__).parent.parent / "examples" / "records.json"
|
|
700
|
+
|
|
701
|
+
# rdump --fields key,title,syntax --csv
|
|
702
|
+
rdump.main([str(example_records_json_path), "--fields", "key,title,syntax", "--csv"])
|
|
703
|
+
captured = capsysbinary.readouterr()
|
|
704
|
+
assert captured.err == b""
|
|
705
|
+
assert captured.out == b"key,title,syntax\r\nQ42eWSaF,A sample pastebin record,text\r\n"
|
|
706
|
+
|
|
707
|
+
# rdump --fields key,title,syntax --csv
|
|
708
|
+
rdump.main([str(example_records_json_path), "--fields", "key,title,syntax", "--csv-no-header"])
|
|
709
|
+
captured = capsysbinary.readouterr()
|
|
710
|
+
assert captured.err == b""
|
|
711
|
+
assert captured.out == b"Q42eWSaF,A sample pastebin record,text\r\n"
|
|
712
|
+
|
|
713
|
+
|
|
694
714
|
if __name__ == "__main__":
|
|
695
715
|
__import__("standalone_test").main(globals())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from flow.record.utils import boolean_argument
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_boolean_argument() -> None:
|
|
7
|
+
assert boolean_argument("True") is True
|
|
8
|
+
assert boolean_argument("true") is True
|
|
9
|
+
assert boolean_argument("trUe") is True
|
|
10
|
+
assert boolean_argument("False") is False
|
|
11
|
+
assert boolean_argument("false") is False
|
|
12
|
+
assert boolean_argument("1") is True
|
|
13
|
+
assert boolean_argument("0") is False
|
|
14
|
+
assert boolean_argument("yes") is True
|
|
15
|
+
assert boolean_argument("no") is False
|
|
16
|
+
assert boolean_argument("y") is True
|
|
17
|
+
assert boolean_argument("n") is False
|
|
18
|
+
assert boolean_argument("on") is True
|
|
19
|
+
assert boolean_argument("off") is False
|
|
20
|
+
assert boolean_argument(True) is True
|
|
21
|
+
assert boolean_argument(False) is False
|
|
22
|
+
assert boolean_argument(1) is True
|
|
23
|
+
assert boolean_argument(0) is False
|
|
24
|
+
with pytest.raises(ValueError, match="Invalid boolean argument: .*"):
|
|
25
|
+
boolean_argument("maybe")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|