Skip to content

test(e2e): add e2e tests for workbooks, datasources, views, pagination, jobs, tagging, projects, favorites, metadata, site-admin - #1823

Open
jacalata wants to merge 9 commits into
developmentfrom
jac/e2e-tests
Open

test(e2e): add e2e tests for workbooks, datasources, views, pagination, jobs, tagging, projects, favorites, metadata, site-admin#1823
jacalata wants to merge 9 commits into
developmentfrom
jac/e2e-tests

Conversation

@jacalata

@jacalata jacalata commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds e2e tests for 10 TSC functional areas: workbooks CRUD, datasources CRUD, views export, pagination, jobs/async, tagging, projects admin, favorites, metadata GraphQL, and site-admin operations
  • Consolidates shared boilerplate (project lookup, server fixture, SSL bypass) into conftest.py; adds server_admin, default_project, project_id session-scoped fixtures and e2e_admin marker
  • All tests validated against a live Tableau server; real failures found and fixed (SSL, xdist incompatibility, extract refresh 403, tagging 404 in finally block, workbook filter flakiness)

Test plan

  • Run pytest test_e2e/ -v -n 0 -m e2e against a Tableau server with TABLEAU_SERVER, TABLEAU_TOKEN_NAME, TABLEAU_TOKEN, TABLEAU_SITE, TABLEAU_PROJECT, TABLEAU_VERIFY_SSL set — 41 passed, 1 known flake (test_datasources_get name-filter timing), 3 skipped pending Add Projects.get_by_path() for hierarchy-aware project lookup #1804 merge (test_projects_get_by_path)
  • Run pytest test_e2e/test_site_admin.py -v -n 0 -m e2e_admin with additional TABLEAU_SITEADMIN_TOKEN_NAME / TABLEAU_SITEADMIN_TOKEN env vars — skipped, test server does not support user creation via REST API
  • Confirm no regressions in existing tests under test/ — CI passing

🤖 Generated with Claude Code

jacalata and others added 5 commits July 21, 2026 15:53
Adds integration tests covering:
- Workbooks CRUD (create, publish, get, update, download, delete)
- Datasources CRUD (publish, get, update connections, refresh, delete)
- Views export (PDF, PNG, CSV, Excel)
- Tag quoting (spaces and commas in tag names)
- Projects get_by_path
- Pagination (Pager and QuerySet, order_by, pagesize)
- Jobs and async operations (trigger refresh, poll until complete)
- Projects admin CRUD and permissions (SiteAdmin)
- Users and groups (SiteAdmin)
- Workbook permissions (grant, verify, revoke)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move default_project, project_id, and server_admin into conftest.py so
individual test files don't each inline the same TABLEAU_PROJECT env var
lookup. Saves ~130 lines of boilerplate across 5 files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- tagging: guard finally delete_tags with try/except (tag already deleted by test body)
- jobs: skip on 403180 when workbook has no extract and refresh is disallowed
- workbooks: improve filter assertion error message; use QuerySet filter instead of get()
- conftest: add TABLEAU_VERIFY_SSL env var to skip SSL verification for on-prem servers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rkbook asset

Restores content from old jac/e2e-tests branch (commit cf59ca4):
- test_favorites.py: workbook/view/datasource favorites
- test_metadata.py: GraphQL metadata API queries
- test_site_admin.py: schedules, webhooks, connection update, data freshness,
  workbook move, user/group management (requires e2e_admin marker)
- assets/WorkbookWithExtract.twbx: binary asset for extract refresh tests
- test_jobs.py: add extract_workbook fixture and test_extract_workbook_refresh_completes
- test_workbooks_crud.py: add test_workbook_populate_preview_image

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
tableauserverclient
   __init__.py50100% 
   config.py150100% 
   datetime_helpers.py2511 96%
   exponential_backoff.py200100% 
   filesys_helpers.py310100% 
   namespace.py2633 88%
tableauserverclient/bin
   __init__.py20100% 
   _version.py358212212 41%
tableauserverclient/helpers
   __init__.py10100% 
   logging.py20100% 
   strings.py3111 97%
tableauserverclient/models
   __init__.py460100% 
   collection_item.py4177 83%
   column_item.py553232 42%
   connection_credentials.py351111 69%
   connection_item.py941414 85%
   custom_view_item.py1442121 85%
   data_acceleration_report_item.py5411 98%
   data_alert_item.py15844 97%
   data_freshness_policy_item.py1551515 90%
   database_item.py2073636 83%
   datasource_item.py3001212 96%
   dqw_item.py10455 95%
   exceptions.py40100% 
   extensions_item.py13244 97%
   extract_item.py4444 91%
   favorites_item.py6988 88%
   fileupload_item.py190100% 
   flow_item.py1491010 93%
   flow_run_item.py710100% 
   group_item.py8966 93%
   groupset_item.py4977 86%
   interval_item.py1823232 82%
   job_item.py1921010 95%
   linked_tasks_item.py7911 99%
   location_item.py2922 93%
   metric_item.py1291313 90%
   oidc_item.py6333 95%
   pagination_item.py3411 97%
   permissions_item.py1111212 89%
   project_item.py2073131 85%
   property_decorators.py1001818 82%
   reference_item.py2622 92%
   revision_item.py5911 98%
   schedule_item.py20966 97%
   server_info_item.py3777 81%
   site_item.py6361313 98%
   subscription_item.py10122 98%
   table_item.py1191818 85%
   tableau_auth.py612525 59%
   tableau_types.py2711 96%
   tag_item.py150100% 
   target.py60100% 
   task_item.py5622 96%
   user_item.py3101818 94%
   view_item.py2201616 93%
   virtual_connection_item.py6488 88%
   webhook_item.py6911 99%
   workbook_item.py3621616 96%
tableauserverclient/server
   __init__.py90100% 
   exceptions.py40100% 
   filter.py2911 97%
   pager.py3311 97%
   query.py1431515 90%
   request_factory.py1335195195 85%
   request_options.py38655 99%
   server.py1882323 88%
   sort.py60100% 
tableauserverclient/server/endpoint
   __init__.py350100% 
   auth_endpoint.py771111 86%
   custom_views_endpoint.py1521212 92%
   data_acceleration_report_endpoint.py210100% 
   data_alert_endpoint.py942323 76%
   databases_endpoint.py1113030 73%
   datasources_endpoint.py3233333 90%
   default_permissions_endpoint.py4433 93%
   dqw_endpoint.py451616 64%
   endpoint.py2122020 91%
   exceptions.py7766 92%
   extensions_endpoint.py310100% 
   favorites_endpoint.py942222 77%
   fileuploads_endpoint.py510100% 
   flow_runs_endpoint.py6299 85%
   flow_task_endpoint.py2122 90%
   flows_endpoint.py1985353 73%
   groups_endpoint.py12699 93%
   groupsets_endpoint.py7277 90%
   jobs_endpoint.py6799 87%
   linked_tasks_endpoint.py370100% 
   metadata_endpoint.py881414 84%
   metrics_endpoint.py5566 89%
   oidc_endpoint.py4211 98%
   permissions_endpoint.py4433 93%
   projects_endpoint.py1782424 87%
   resource_tagger.py1273535 72%
   schedules_endpoint.py1191111 91%
   server_info_endpoint.py361010 72%
   sites_endpoint.py1302727 79%
   subscriptions_endpoint.py561414 75%
   tables_endpoint.py1103636 67%
   tasks_endpoint.py6366 90%
   users_endpoint.py18388 96%
   views_endpoint.py15099 94%
   virtual_connections_endpoint.py1131010 91%
   webhooks_endpoint.py5499 83%
   workbooks_endpoint.py3382222 93%
TOTAL12007142388% 

…urce tests

SampleDS.tds is a live connection to postgres91.test.tsi.lan which Tableau
Server tries to validate on publish, causing the fixture to fail if the host
is unreachable. WorldIndicators.tdsx is an extract-based datasource that
publishes without a connection check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bcantoni

bcantoni commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Claude code report:

Code Review

Reviewed the diff via gh pr diff 1823. Overview: adds ~1,650 lines of e2e tests across 10 functional areas (workbooks CRUD, datasources CRUD, views export, pagination, jobs, tagging expansion, projects admin, favorites, metadata GraphQL, site-admin), plus binary fixture assets and conftest.py consolidation (server_admin, default_project, project_id fixtures, new e2e_admin marker). Solid, well-organized addition to test_e2e/, which is correctly excluded from the default pytest test -n auto run.

Blocking: will fail CI black check

  • test_e2e/test_views_export.py (a_view fixture): if not views: pytest.fail("Published workbook has no views") is a single-line if. Confirmed locally with black --check --line-length 120 — this gets reformatted to a multi-line block, so meta-checks.yml's black --check will fail on this line as written.

Suggestions (non-blocking)

  • test_workbook_permissions.py's workbook_and_user fixture still does manual TABLEAU_PROJECT project-lookup instead of using the new project_id/default_project fixtures this PR introduced — worth using them for consistency with the dedup work done elsewhere (e.g. test_tagging.py).
  • Several except Exception: pass best-effort cleanup blocks (test_datasources_crud.py, test_pagination.py's datasource fixture, test_users_groups.py, etc.) silently swallow teardown failures. test_pagination.py's workbook cleanup already does warnings.warn(...) on failure — consider applying that pattern more consistently instead of bare pass.
  • Minor stylistic inconsistency: SAMPLE_DATASOURCE is passed as a bare Path in some tests and wrapped in str(...) in others (e.g. test_favorites.py, test_jobs.py). Not a bug since publish()'s PathOrFileR accepts both, but worth picking one convention.

Other notes

  • e2e_admin marker is registered via conftest.py's pytest_configure, but not added to pyproject.toml's [tool.pytest.ini_options] markers list (which only has e2e and packaging) — fine as-is, just flagging for consistency if marker registration is ever centralized there.
  • test_get_by_path_raises_for_empty_or_root_path in test_projects_get_by_path.py intentionally has no pytestmark/marker (documented in a module comment, since the file mixes e2e and e2e_admin tests) — harmless since it doesn't use server fixtures, but worth being aware it'll run under any marker selection.
  • No security concerns: no hardcoded credentials, tokens come from env vars per existing convention, TABLEAU_VERIFY_SSL defaults to "true" (secure by default).
  • Coverage is a clear net positive — exercises real server behavior (SSL, xdist incompatibility, live 403/404s) that mocked unit tests can't catch.

🤖 Generated with Claude Code

@bcantoni

Copy link
Copy Markdown
Contributor

@jacalata overall this looks good. Take a look at the issues Claude noted to see if any are worth fixing/improving.

jacalata and others added 2 commits July 28, 2026 00:21
- Reformat single-line `if` in test_views_export.py's a_view fixture so black
  --check passes CI (was blocking meta-checks.yml).
- Use conftest.default_project in test_workbook_permissions.workbook_and_user,
  eliminating 10 lines of duplicated project-lookup logic.
- Replace bare `except Exception: pass` in teardown blocks with
  warnings.warn(...) for consistency with test_pagination.py's workbook cleanup.
- Use bare Path for SAMPLE_DATASOURCE calls across test_favorites.py and
  test_jobs.py (was inconsistently wrapped in str()).
- Register the e2e_admin marker in pyproject.toml so it matches
  conftest.py's pytest_configure.

All from bcantoni's review comment on #1823.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jacalata

Copy link
Copy Markdown
Contributor Author

Thanks — fixes applied in 3a4fe13:

  1. Split the single-line if not views: pytest.fail(...) in test_views_export.py's a_view fixture so black --check passes CI.
  2. test_workbook_permissions.workbook_and_user now takes the default_project fixture from conftest.py; dropped 15 lines of duplicated project-lookup logic.
  3. Replaced 7 bare except Exception: pass teardown blocks across test_datasources_crud.py, test_pagination.py, test_projects_admin.py, test_tagging.py with warnings.warn(...), matching the pattern in test_pagination.py's workbook cleanup.
  4. Dropped str(...) wrapping on SAMPLE_DATASOURCE in test_favorites.py and test_jobs.py; bare Path is consistent with the rest of the branch now.
  5. Registered the e2e_admin marker in pyproject.toml matching conftest.py.

Skipped Claude's suggestion to also touch test_workbook_permissions.py:149 — that one's a test-body finally, not a fixture teardown.

@jacalata
jacalata requested a lite review from Copilot August 6, 2026 21:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a comprehensive end-to-end (E2E) pytest suite that exercises Tableau Server Client (TSC) APIs against a real Tableau server, including a separate set of SiteAdmin-only tests.

Changes:

  • Introduces multiple new E2E test modules covering CRUD, permissions, exports, jobs, pagination, favorites, metadata, and admin operations.
  • Adds e2e_admin marker plus a server_admin authenticated fixture and shared project fixtures for consistent publishing targets.
  • Expands tagging tests to include datasource tagging scenarios and additional space/comma edge cases.

Reviewed changes

Copilot reviewed 15 out of 17 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test_e2e/conftest.py Adds admin-auth session fixture, shared project selection, and SSL verify configuration for E2E runs.
test_e2e/test_workbooks_crud.py New workbook CRUD E2E coverage (publish/get/update/populate/download/delete).
test_e2e/test_workbook_permissions.py New workbook/view permissions E2E coverage using SiteAdmin credentials.
test_e2e/test_views_export.py New view export E2E coverage (PDF/PNG/CSV).
test_e2e/test_users_groups.py New SiteAdmin E2E coverage for user/group lifecycle and membership operations.
test_e2e/test_tagging.py Updates tagging tests; adds datasource tagging and more tag edge-case coverage.
test_e2e/test_site_admin.py New SiteAdmin E2E coverage for schedules, webhooks, connection updates, freshness policy, project moves, and groups.
test_e2e/test_projects_get_by_path.py New E2E/admin split tests for projects.get_by_path() behavior.
test_e2e/test_projects_admin.py New SiteAdmin project CRUD + default permissions E2E coverage.
test_e2e/test_pagination.py New E2E coverage for TSC.Pager and QuerySet pagination behavior.
test_e2e/test_metadata.py New E2E smoke tests for Metadata GraphQL queries.
test_e2e/test_jobs.py New E2E coverage for refresh jobs and wait_for_job() completion paths.
test_e2e/test_favorites.py New E2E coverage for favorites on workbooks/views/datasources.
test_e2e/test_datasources_crud.py New datasource CRUD E2E coverage (publish/get/update/connections/tags/download/delete).
pyproject.toml Registers the new e2e_admin marker.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +81 to +85
def test_workbook_download_returns_nonempty_bytes(server, published_workbook):
"""download() writes a non-empty file into a BytesIO buffer."""
buffer = io.BytesIO()
server.workbooks.download(published_workbook.id, filepath=buffer)
assert buffer.tell() > 0
Comment thread test_e2e/test_workbook_permissions.py Outdated
Comment on lines +73 to +79
finally:
# Clean up: build the delete rule directly to avoid masking the original exception
delete_rule = TSC.PermissionsRule(
grantee,
{TSC.Permission.Capability.Read: TSC.Permission.Mode.Allow},
)
server_admin.workbooks.delete_permission(workbook, delete_rule)
Comment thread test_e2e/conftest.py
Comment on lines +30 to +31
http_options = {"verify": os.environ.get("TABLEAU_VERIFY_SSL", "true").lower() != "false"}
server = TSC.Server(url, use_server_version=True, http_options=http_options)
Comment thread test_e2e/test_users_groups.py Outdated
Comment on lines +32 to +35
new_user = TSC.UserItem("tsc-e2e-user", TSC.UserItem.Roles.Unlicensed)
created = None
try:
created = server_admin.users.add(new_user)
Comment on lines +54 to +57
def test_favorites_view(server, workbook):
"""A view can be added to and removed from favorites."""
server.workbooks.populate_views(workbook)
view = workbook.views[0]
Comment on lines +35 to +41
schedule = TSC.ScheduleItem(
_name("tsc-e2e-schedule"),
50,
TSC.ScheduleItem.Type.Extract,
TSC.ScheduleItem.ExecutionOrder.Parallel,
interval,
)
Copilot review findings on #1823.

(1) test_users_groups.py: every user/group name is now suffixed with a
uuid via a _unique(prefix) helper. Fixed names caused 409 conflicts
when two runs executed against the same site in parallel, or when a
prior run crashed before its finally block removed the resource.

(2) test_workbook_permissions.py: wrap the delete_permission call in
test_update_permissions_appears_on_populate's finally block in
try/except, matching the sibling test_delete_permission_removes_rule.
Unwrapped cleanup can raise (e.g. rule already gone, transient server
error) and mask the original assertion failure with a confusing
"delete_permission raised 500" instead of the actual test failure.

No production code touched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants