From c89eeb451365b02f8d0e2a737dba6649aa9bee78 Mon Sep 17 00:00:00 2001 From: scarreras Date: Mon, 2 Nov 2026 03:42:34 -0300 Subject: [PATCH] test(sdkharness): make the files.list check detect a client that ignores ls --recursive The check uploaded only flat names, so `ls --recursive` and `ls` returned the same listing and a CLI that ignored the option still passed. Upload a nested name (list/sub/d.txt): the recursive listing must contain it, and the listing without --recursive must collapse it into the folder entry list/sub/. Co-Authored-By: Claude Sonnet 5.5 --- .../tests/conformance-file-lifecycle.py | 24 ++++++++++++++++--- ...arness-files-list-nested.infrastructure.md | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 changelog.d/+sdkharness-files-list-nested.infrastructure.md diff --git a/.sdkharness/tests/conformance-file-lifecycle.py b/.sdkharness/tests/conformance-file-lifecycle.py index 08c421391..f55bebe07 100755 --- a/.sdkharness/tests/conformance-file-lifecycle.py +++ b/.sdkharness/tests/conformance-file-lifecycle.py @@ -363,10 +363,12 @@ def assert_version_absent(self, bucket_name: str, name: str, file_id: str, step: def files_list(self) -> None: payload = b'l' * 256 second = b'L' * 300 - names = [self.object_name(f'list/{leaf}.txt') for leaf in ('a', 'b', 'c')] + top_level = [self.object_name(f'list/{leaf}.txt') for leaf in ('a', 'b', 'c')] + nested = [self.object_name(f'list/sub/{leaf}.txt') for leaf in ('d', 'e')] + names = [*top_level, *nested] for name in names: self.upload(f'upload {Path(name).stem}', payload, name) - self.upload('upload c again', second, names[-1]) + self.upload('upload c again', second, top_level[-1]) outsider = self.object_name('outside.txt') self.upload('upload outsider', payload, outsider) @@ -381,7 +383,23 @@ def files_list(self) -> None: if listed != set(names): raise CheckFailure('list names', 'prefix listing differs from uploaded names') - versions = self.list_versions(names[-1]) + # Without --recursive the nested names must collapse into one folder entry; with it, + # every name is listed (above; --json shows a folder as its first file, so two nested + # names also tell the two modes apart there). A client that ignores the option fails + # one or the other. The plain listing is read, because --json hides the folder entry. + raw = self.invoke( + 'list one level', + 'ls', + f'b2://{self.bucket_name}/{self.object_name("list/")}', + ) + one_level = {line.strip() for line in raw.splitlines() if line.strip()} + folder = self.object_name('list/sub/') + if one_level != {*top_level, folder}: + raise CheckFailure( + 'list one level', 'listing without --recursive did not collapse the nested names' + ) + + versions = self.list_versions(top_level[-1]) ids = {str(item.get('fileId', '')) for item in versions} if len(versions) != 2 or len(ids) != 2 or '' in ids: raise CheckFailure('list versions', 'twice-uploaded key did not return two versions') diff --git a/changelog.d/+sdkharness-files-list-nested.infrastructure.md b/changelog.d/+sdkharness-files-list-nested.infrastructure.md new file mode 100644 index 000000000..919e7d310 --- /dev/null +++ b/changelog.d/+sdkharness-files-list-nested.infrastructure.md @@ -0,0 +1 @@ +Make the `.sdkharness` `files.list` conformance check upload a nested name, so a client that ignores `ls --recursive` fails it.