From a0ec216598b2f08dc0f60dab794ae190a8e225c0 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Fri, 18 Sep 2026 13:56:22 -0700 Subject: [PATCH 1/4] test(e2e): round-trip CSVImport bug coverage for #1811 Adds a two-row CSV fixture and one createsiteusers e2e test exercising every UserItem.CSVImport bug fixed in TSC #1811 (issue #1809): AUTH column readable at 8 columns, mixed-case usernames preserved, TableauIDWithMFA in the auth allowlist, case-insensitive validation of role/admin/publisher tokens, and the @property_is_enum guard active on the parsed auth_setting. Test runs under the same server-admin / site-admin gate as test_users_create_site_users and shares its ordering slot; skips cleanly when the caller lacks admin scope or a credentials file. Related: #297 (license-name case sensitivity still surfacing after partial fixes), #434 (Local auth accepted by the CLI but crashes on TSC call). --- tests/assets/users_csvimport_bugs_1811.csv | 2 ++ tests/e2e/online_tests.py | 27 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/assets/users_csvimport_bugs_1811.csv diff --git a/tests/assets/users_csvimport_bugs_1811.csv b/tests/assets/users_csvimport_bugs_1811.csv new file mode 100644 index 00000000..13f50258 --- /dev/null +++ b/tests/assets/users_csvimport_bugs_1811.csv @@ -0,0 +1,2 @@ +UserMixedCase_1811_A@example.com, ,"Mixed Case LDAP User 1811A",Viewer,none,no,mixedcase_1811_a@example.com,TableauID +UserMixedCase_1811_B@example.com, ,"MFA User 1811B",Creator,none,no,mfa_1811_b@example.com,TableauIDWithMFA diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index 8f1d50d6..95f3af59 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -74,6 +74,7 @@ def get_publishable_name(file_value: str) -> str: TWB_FILE_WITH_EMBEDDED_CONNECTION = "EmbeddedCredentials.twb" USERS_DETAILS_FILE = "detailed_users.csv" + USERS_CSVIMPORT_1811_FILE = "users_csvimport_bugs_1811.csv" USERNAMES_FILE = "usernames.csv" @@ -316,6 +317,32 @@ def test_users_create_site_users(self): arguments = [command, users, "--role", "Publisher"] _test_command(arguments) + @pytest.mark.order(2) + def test_users_create_site_users_csvimport_bugs_1811(self): + """Round-trip coverage for the six ``UserItem.CSVImport`` bugs fixed in + tableau/server-client-python#1811 (issue #1809). Once tabcmd delegates + its CSV stack to TSC's ``CSVImport``, this fixture is the regression + guarantee that the delegation preserved each fix. + + Exercises, in one two-row CSV: + + * the 8-column AUTH bound (row length now readable), + * mixed-case usernames preserved verbatim (case-sensitive auth systems), + * ``TableauIDWithMFA`` accepted in the auth allowlist, + * capitalised role/admin/publisher tokens accepted case-insensitively, + * the ``@property_is_enum(Auth)`` guard active on the parsed + ``UserItem.auth_setting``. + + Related: tabcmd #297 (license-name case sensitivity), #434 (``Local`` + auth accepted by the CLI but rejected server-side). + """ + if not server_admin and not site_admin: + pytest.skip("Must be server or site administrator to create site users") + command = "createsiteusers" + users = os.path.join("tests", "assets", TestAssets.USERS_CSVIMPORT_1811_FILE) + arguments = [command, users, "--role", "Publisher"] + _test_command(arguments) + @pytest.mark.order(3) def test_group_creategroup(self): if not server_admin and not site_admin: From e79ce21f17e7f784a1dca793e16b41f6423a43b3 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Fri, 18 Sep 2026 17:21:53 -0700 Subject: [PATCH 2/4] test(e2e): pass --no-complete + drop --role Publisher on CSVImport 1811 test `createsiteusers` runs `--complete` by default, which invokes tabcmd's duplicate strict validator (`UserCommand._validate_user_or_throw`). That validator rejects `len(line) > 7` and validates license values case-sensitively -- both of which the CSVImport #1811 fixture is specifically meant to exercise. Under `--complete`, the CSV never reached TSC's `UserItem.CSVImport` and the test fell through tabcmd-side. `--no-complete` skips tabcmd's validator so the fixture reaches TSC's parser. Removing `--role Publisher` lets each row's own license field be validated -- restoring coverage of the case-insensitive-license fix. Also corrected the docstring: five of the six bugs, since the sixth (unknown-AUTH rejection) is a negative-path check that doesn't fit a single happy-path fixture and is already covered by TSC's unit tests. --- tests/e2e/online_tests.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index 95f3af59..4c38d1fc 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -319,10 +319,10 @@ def test_users_create_site_users(self): @pytest.mark.order(2) def test_users_create_site_users_csvimport_bugs_1811(self): - """Round-trip coverage for the six ``UserItem.CSVImport`` bugs fixed in - tableau/server-client-python#1811 (issue #1809). Once tabcmd delegates - its CSV stack to TSC's ``CSVImport``, this fixture is the regression - guarantee that the delegation preserved each fix. + """Round-trip coverage for five of the six ``UserItem.CSVImport`` bugs + fixed in tableau/server-client-python#1811 (issue #1809). Once tabcmd + delegates its CSV stack to TSC's ``CSVImport``, this fixture is the + regression guarantee that the delegation preserved each fix. Exercises, in one two-row CSV: @@ -333,6 +333,15 @@ def test_users_create_site_users_csvimport_bugs_1811(self): * the ``@property_is_enum(Auth)`` guard active on the parsed ``UserItem.auth_setting``. + The sixth bug (unknown-AUTH rejection) is a negative-path check that + doesn't fit a single happy-path fixture; TSC's own unit tests cover it. + + ``--no-complete`` skips tabcmd's duplicate strict validator so the CSV + actually reaches TSC's ``UserItem.CSVImport`` — which is what this test + is meant to exercise. No ``--role`` override, so the role field in each + row is what gets validated (that's the point of the case-insensitivity + check). + Related: tabcmd #297 (license-name case sensitivity), #434 (``Local`` auth accepted by the CLI but rejected server-side). """ @@ -340,7 +349,7 @@ def test_users_create_site_users_csvimport_bugs_1811(self): pytest.skip("Must be server or site administrator to create site users") command = "createsiteusers" users = os.path.join("tests", "assets", TestAssets.USERS_CSVIMPORT_1811_FILE) - arguments = [command, users, "--role", "Publisher"] + arguments = [command, users, "--no-complete"] _test_command(arguments) @pytest.mark.order(3) From c19a43b5973b97ea8ecb28d8fb132bce9bcc4a53 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Fri, 23 Oct 2026 18:30:26 -0700 Subject: [PATCH 3/4] docs: correct test docstring -- this test doesn't reach TSC's CSVImport yet createsiteusers parses the CSV itself via UserCommand.get_users_from_file() / _parse_line() and builds TSC.UserItem directly; it doesn't delegate to TSC's UserItem.CSVImport. The docstring claimed the opposite. Co-Authored-By: Claude Code --- tests/e2e/online_tests.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/e2e/online_tests.py b/tests/e2e/online_tests.py index 4c38d1fc..a2229045 100644 --- a/tests/e2e/online_tests.py +++ b/tests/e2e/online_tests.py @@ -337,10 +337,13 @@ def test_users_create_site_users_csvimport_bugs_1811(self): doesn't fit a single happy-path fixture; TSC's own unit tests cover it. ``--no-complete`` skips tabcmd's duplicate strict validator so the CSV - actually reaches TSC's ``UserItem.CSVImport`` — which is what this test - is meant to exercise. No ``--role`` override, so the role field in each - row is what gets validated (that's the point of the case-insensitivity - check). + reaches tabcmd's own row parser (``UserCommand.get_users_from_file`` / + ``_parse_line``), which builds the ``TSC.UserItem`` directly -- tabcmd + doesn't delegate to TSC's ``UserItem.CSVImport`` yet, so this exercises + the same validation rules #1811 fixed, via tabcmd's current parsing + path rather than through ``CSVImport`` itself. No ``--role`` override, + so the role field in each row is what gets validated (that's the point + of the case-insensitivity check). Related: tabcmd #297 (license-name case sensitivity), #434 (``Local`` auth accepted by the CLI but rejected server-side). From 7e2b8ca932e32f4983ad0a02c82a84eeaa320d22 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Mon, 26 Oct 2026 13:21:20 -0700 Subject: [PATCH 4/4] test(e2e): use a valid TSC auth value in the CSVImport fixture TableauID is not a member of TSC.UserItem.Auth in the pinned tableauserverclient (0.42), so the first row raised ValueError before the server call. Use ServerDefault for that row. Co-Authored-By: Claude Code --- tests/assets/users_csvimport_bugs_1811.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/assets/users_csvimport_bugs_1811.csv b/tests/assets/users_csvimport_bugs_1811.csv index 13f50258..c63ae1bc 100644 --- a/tests/assets/users_csvimport_bugs_1811.csv +++ b/tests/assets/users_csvimport_bugs_1811.csv @@ -1,2 +1,2 @@ -UserMixedCase_1811_A@example.com, ,"Mixed Case LDAP User 1811A",Viewer,none,no,mixedcase_1811_a@example.com,TableauID +UserMixedCase_1811_A@example.com, ,"Mixed Case LDAP User 1811A",Viewer,none,no,mixedcase_1811_a@example.com,ServerDefault UserMixedCase_1811_B@example.com, ,"MFA User 1811B",Creator,none,no,mfa_1811_b@example.com,TableauIDWithMFA