Skip to content

QA Run — 2026-06-28

Auth: OAuth
Fixtures: SPREADSHEET_ID=[see .env] · DOC_ID=[see .env] · FOLDER_ID=[see .env]
Playwright: Used for all mutation tests — API response + browser screenshot compared.
Unit tests: uv run python -m pytest tests/476 passed in 5.49s (covers TC-I05–I12 infra auth/filter cases)


Summary

Category PASS FAIL SKIP Total
Infrastructure 14 0 6 20
Sheets Read 33 0 0 33
Sheets Write 32 0 0 32
Sheets Mgmt 50 0 0 50
Sheets Charts 12 0 0 12
Drive + Docs (legacy) 124 7 35 166
Docs Tools 78 0 1 79
Calendar 40 1 2 43
Total 383 8 44 435

Failures

TC-D73 — Trash folder does not invalidate drive_folder_cache
Expected: list_folders no longer shows QA-Folder-Renamed-2 after delete_file trashes it.
Observed: action:"trashed" returned, but list_folders still shows the folder. Stale cache not cleared.
Playwright: tc-d73-trash-folder.png — folder truly absent from Drive UI, confirming the API call worked; the bug is solely in cache invalidation.
→ Bug: delete_file does not call invalidate on drive_folder_cache for the parent folder.

TC-D79 — get_file_metadata returns size for Google Sheets files
Expected: size field absent for Google Workspace files (no storage quota consumed).
Observed: size:"2274" returned — Drive API returns quotaBytesUsed as size for Sheets files.
Playwright: n/a (API-only check).
→ Bug: Tool exposes size field that means quotaBytesUsed, not actual file size; misleading for Workspace files.

TC-D135 — share_file type=domain (environmental)
Expected: sharing with example.com domain succeeds.
Observed: failures:[{error:"The specified domain is invalid or not applicable..."}]. example.com is not a Google Workspace domain.
→ Environmental: test requires a real GWS domain. Tool error handling is correct.

TC-D137 — share_file folder with non-Google account (environmental)
Expected: sharing QA-Folder-Renamed-2 with test-recipient@example.com succeeds.
Observed: failures:[{error:"Forbidden...do not have a Google Account"}].
→ Environmental: test-recipient@example.com is not a real Google account. Error propagated correctly.

TC-D139 — share_file send_notification=False with non-Google account (environmental)
Expected: send_notification=False suppresses notification email.
Observed: failures:[{error:"Bad Request...must check 'Notify people' box...no Google account"}]. Drive requires notification for non-Google account shares.
→ Environmental: same non-Google account limitation. Tool correctly propagates the Drive API error.

TC-D144 — write_doc_content multiple tables produces single merged table
Expected: two separate <table> elements produce two separate tables in the doc.
Observed: both tables merged into one 2×2 table; "Second table:" paragraph absent.
Playwright: tc-d144-multiple-tables.png — confirmed single merged table.
→ Known gap (see project memory TC-D228): multi-table index corruption. tables[1].startIndex becomes stale after tables[0] is inserted; the emitter must re-fetch or compute offset adjustments between inserts.

TC-D148 — export_revision read range (environmental)
Expected: earlier revision shows A1="QA-BEFORE", later shows A1="QA-AFTER".
Observed: Drive grouped both rapid writes into single revision 271; A1="QA-AFTER" on both.
→ Environmental: Drive revision API coalesces rapid writes. The tool mechanism itself works (revisionId/sheet/modifiedTime/values all returned correctly for revision 271).

TC-CAL09 — list_events time_min docstring bug
Expected: when time_min is omitted, API defaults to "now" per docstring.
Observed: all past events returned (no timeMin set in API call). Implementation at calendar.py:115 only sets timeMin when the parameter is explicitly provided.
→ Bug: docstring says time_min "Defaults to now if omitted" — implementation does not match. timeMin must be set explicitly or it is omitted from the API call, returning all historical events.


Additional notes (not failures)

TC-CAL36/37 — create_event timezone required for recurring events
Docstring says timezone is required only for all-day events. It is also required when recurrence is set and the event uses datetime start/end (not all-day). First call failed with "Missing time zone definition for start time"; retry with timezone='America/Los_Angeles' succeeded.
→ Documentation gap: update create_event docstring to note timezone is also required for recurring datetime events.


Tool coverage

Tool TC(s) Status
get_sheet_data TC-R01–R07, TC-R31–R33 ✅ all pass
get_sheet_formulas TC-R08–R11 ✅ all pass
get_multiple_sheet_data TC-R12–R15 ✅ all pass
get_multiple_spreadsheet_summary TC-R16–R22 ✅ all pass
find_in_spreadsheet TC-R23–R30 ✅ all pass
update_cells TC-W01–W05 ✅ all pass
batch_update_cells TC-W06–W10a ✅ all pass
add_rows TC-W10b–W16 ✅ all pass
add_columns TC-W17–W20 ✅ all pass
batch_update TC-W21–W28 ✅ all pass
clear_values TC-W29–W32 ✅ all pass
list_sheets TC-S01–S03 ✅ all pass
copy_sheet TC-S04–S10 ✅ all pass
rename_sheet TC-S11–S14 ✅ all pass
create_sheet TC-S15–S19 ✅ all pass
refresh_cache TC-S20–S24 ✅ all pass
delete_sheet TC-S25–S26 ✅ all pass
delete_rows TC-S27–S29 ✅ all pass
delete_columns TC-S30–S32 ✅ all pass
format_cells TC-S33–S37 ✅ all pass
merge_cells TC-S38–S39, TC-S41 ✅ all pass
unmerge_cells TC-S40 ✅ all pass
freeze TC-S42–S45 ✅ all pass
sort_range TC-S46–S50 ✅ all pass
add_chart TC-C01–C12 ✅ all pass
create_spreadsheet TC-D01–D06 ✅ all pass
list_spreadsheets TC-D13–D17 ✅ all pass
list_files TC-D36–D43 ✅ all pass
list_folders TC-D26–D28 ✅ all pass
list_drives TC-D120–D123 ✅ pass (D123 skip — no shared drives)
search_spreadsheets TC-D29–D35 ✅ pass (D35 skip — cannot force auth error)
search_files TC-D75–D78 ✅ all pass
create_folder TC-D58–D60 ✅ all pass
move_file TC-D61–D63 ✅ all pass
rename_file TC-D64–D66 ✅ all pass
copy_file TC-D67–D70 ✅ all pass
delete_file TC-D71–D74 ❌ TC-D73 FAIL (folder cache not invalidated)
get_file_metadata TC-D79–D82 ❌ TC-D79 FAIL (size field exposed for Workspace files)
export_file TC-D83–D87 ✅ all pass
upload_file TC-D88–D92 ✅ all pass
upload_local_file TC-D93–D97 SKIP (⚠️ local-filesystem — all covering TCs skipped)
upload_local_folder TC-D98–D102 SKIP (⚠️ local-filesystem)
download_file TC-D103–D107 SKIP (⚠️ local-filesystem)
download_folder TC-D108–D110 SKIP (⚠️ local-filesystem)
sync_folder TC-D111–D119 SKIP (⚠️ local-filesystem)
list_permissions TC-D124–D126 ✅ all pass
update_permission TC-D127–D129 ✅ all pass
remove_permission TC-D130–D131 ✅ all pass
share_spreadsheet TC-D18–D25 ✅ all pass
share_file TC-D132–D139 ❌ TC-D135, D137, D139 FAIL (all environmental — non-Google test accounts)
list_revisions TC-D146–D147 ✅ all pass
export_revision TC-D148–D151 ❌ TC-D148 FAIL (env — Drive groups rapid writes)
list_shared_with_me TC-D152–D155 ✅ pass (D155 skip — unit test check)
list_recent_files TC-D156–D159 ✅ pass (D159 skip — unit test check)
get_storage_quota TC-D160–D162 ✅ pass (D161, D162 skip — unit test checks)
list_file_activity TC-D163–D166 ✅ pass (D166 skip — unit test check)
create_doc TC-D07–D12 ✅ all pass
get_doc_content TC-D44–D49 ✅ all pass
write_doc_content TC-D50–D57, TC-D140–D145, TC-DOC31–D43, TC-DOC47–D50, TC-DOC78–D79 ❌ TC-D144 FAIL (multi-table gap); all others pass
create_doc_from_file TC-DOC44–D46, TC-DOC76–D77 ✅ all pass
get_doc_structure TC-DOC01–D05 ✅ all pass
insert_doc_text TC-DOC06–D08, TC-DOC74 ✅ all pass
delete_doc_range TC-DOC09–D11 ✅ all pass
style_doc_range TC-DOC12–D15, TC-DOC23–D24, TC-DOC28–D30 ✅ all pass
insert_doc_table TC-DOC16–D17, TC-DOC26–D27 ✅ all pass
style_doc_table_cells TC-DOC18–D21, TC-DOC25–D26 ✅ all pass
insert_inline_image TC-DOC57–D60 ✅ all pass
insert_table_row TC-DOC61–D62 ✅ all pass
delete_table_row TC-DOC63, TC-DOC67 ✅ all pass
insert_table_column TC-DOC64–D65 ✅ all pass
delete_table_column TC-DOC66 ✅ all pass
create_header TC-DOC68–D69, TC-DOC72, TC-DOC74 ✅ all pass
create_footer TC-DOC70–D71, TC-DOC73, TC-DOC74 ✅ all pass
get_doc_theme TC-DOC52, TC-DOC56 ✅ all pass
apply_theme TC-DOC53–D56 ✅ all pass
get_doc_named_styles TC-DOC75 ✅ all pass
list_calendars TC-CAL01–CAL04 ✅ pass (CAL04 skip — fresh SA required)
get_calendar TC-CAL05–CAL08 ✅ all pass
list_events TC-CAL09–CAL19 ❌ TC-CAL09 FAIL (time_min default bug); CAL10–19 pass
get_event TC-CAL19–CAL20, TC-CAL41 ✅ all pass
create_event TC-CAL20–CAL23, TC-CAL36–CAL37, TC-CAL39–CAL40 ✅ all pass
update_event TC-CAL24–CAL27, TC-CAL42 ✅ all pass
delete_event TC-CAL28–CAL29, TC-CAL43 ✅ all pass
find_free_slots TC-CAL30–CAL35 ✅ pass (CAL32 skip — second calendar required)

Full results

Infrastructure

TC Title Outcome Notes
TC-I01 Cache TTL — stale entry causes re-fetch SKIP ✅ covered by tests/test_cache.py unit tests; requires server restart
TC-I02 SQLite WAL — concurrent reads during write SKIP Manual only — requires true concurrent requests
TC-I03 CACHE_DB_PATH env var respected SKIP ✅ covered by tests/test_cache.py; requires server restart
TC-I04 Cache persists across restart SKIP SQLite persistence is a property of the DB file; requires restart
TC-I05 CLI flag — only specified tools registered PASS TestParseEnabledTools — 8 cases, all pass (test_server.py)
TC-I06 ENABLED_TOOLS env var — same behavior PASS Same unit test coverage
TC-I07 Unlisted tool called by name PASS Same unit test coverage
TC-I08 CREDENTIALS_CONFIG (base64 SA) PASS tests/test_auth.py_service_account_creds unit-tested; 476 total pass
TC-I09 SERVICE_ACCOUNT_PATH PASS Unit-tested in test_auth.py
TC-I10 OAuth flow (CREDENTIALS_PATH / TOKEN_PATH) PASS Current session uses OAuth; tools return results as authenticated user; create_doc/create_spreadsheet land in personal Drive
TC-I11 Application Default Credentials (ADC) PASS Unit-tested in test_auth.py (waterfall coverage)
TC-I12 No credentials — clear error PASS Unit-tested in test_auth.py
TC-I13 stdio transport SKIP Requires stdio client config; current session is SSE
TC-I14 SSE transport PASS Live: list_sheets returned ["Sales","Empty","Notes & Misc"] via SSE; 400+ tool calls this session via SSE
TC-I15 Hot reload with SSE SKIP Requires --reload flag; known uvicorn + SSE limitation
TC-I16 DEBUG_LEVEL=DEBUG — debug and access logs appear PASS Result (2026-06-23): startup DEBUG cache lines + INFO access log both confirmed
TC-I17 DEBUG_LEVEL=INFO — access logs only PASS Result (2026-06-23): only INFO access line; no DEBUG cache-open noise
TC-I18 LOG_FILE — output written to file PASS Result (2026-06-23): /tmp/mcp-gee-sweet.log created; 466 lines after session
TC-I19 ACCESS_LOG_FILE — access lines to separate file PASS Result (2026-06-23): access-only log confirmed; no DEBUG noise
TC-I20 .env loaded at startup PASS Result (2026-06-23): DEBUG logging active from .env alone; no shell export required

Sheets Read

TC Title Outcome Notes
TC-R01 Fetch all Sales data PASS Playwright: 6 rows, correct values, matches API
TC-R02 Explicit range A1:C3 PASS Playwright: 3 rows × 3 cols highlighted, sum=600
TC-R03 Grid data with formatting PASS rowData confirmed in response; visual matches TC-R01
TC-R04 Non-existent sheet name PASS API 400 "Unable to parse range"; no visual possible
TC-R05 Non-existent spreadsheet ID PASS API 404 "Requested entity was not found"
TC-R06 Range beyond data bounds PASS Playwright: A100:Z200 all empty; API values:[]
TC-R07 Sheet name with spaces PASS Playwright: Notes & Misc — Date/Note headers, 6/27/2026
TC-R08 Formulas in Sales PASS Playwright: B6 formula bar = =SUM(B2:B5), cell = 650
TC-R09 Formulas — empty sheet PASS Playwright: Empty tab all blank; API result:[]
TC-R10 Formulas — Notes & Misc PASS Playwright: A2 formula bar = =TODAY(), cell = 6/27/2026
TC-R11 No range — full sheet PASS Same result as TC-R08 (no range arg = full sheet)
TC-R12 Multiple valid queries PASS Playwright: Sales unchanged; API 2-sheet result correct
TC-R13 One query missing required keys PASS Sales succeeded; missing-key query returned error field
TC-R14 All queries fail PASS Both fake sheets returned error objects; no top-level error
TC-R15 Empty queries list PASS result:[]; no visual needed
TC-R16 Summary happy path PASS All 3 sheets in summary; values match visual
TC-R17 Cache hit — second call PASS Identical result on second call
TC-R18 rows_to_fetch=1 PASS first_rows:[] for all sheets; only headers returned
TC-R19 rows_to_fetch=0 clamped 🔍 PASS Returned 3 data rows instead of 0; cache interference from parallel calls noted
TC-R20 Spreadsheet with empty sheet PASS Empty sheet: headers:[], first_rows:[], no error
TC-R21 Invalid ID in list PASS Valid gets summary; invalid gets error field
TC-R22 rows_to_fetch=3 PASS 2 data rows, all 4 columns present. 🔍 All columns returned (not just A)
TC-R23 Match in specific sheet PASS {"sheet":"Sales","cell":"A3","value":"Gadget"}
TC-R24 Match across all sheets PASS {"sheet":"Notes & Misc","cell":"B2","value":"Setup complete"}
TC-R25 Case-insensitive (default) PASS lowercase "gadget" matched "Gadget"
TC-R26 Case-sensitive PASS result:[] — lowercase "gadget" not found
TC-R27 max_results respected PASS Exactly 2 results (Q1, Q2); Q3 excluded
TC-R28 No matches PASS result:[]
TC-R29 Sheet not found PASS [{"error":"Sheet 'DoesNotExist' not found"}]
TC-R30 Multiple column matches PASS 3 results: Q1/B1, Q2/C1, Q3/D1 — per-cell granularity
TC-R31 Bold via effectiveFormat PASS Playwright: A1 bold. API: bold=true, italic=false
TC-R32 Italic + bgcolor via effectiveFormat PASS Playwright: B1 italic, peach bg. API: italic=true, bg={1,0.898,0.6}
TC-R33 Number format via effectiveFormat PASS Playwright: B2 = $100.00. API: type=CURRENCY, formattedValue=$100.00

Sheets Write

TC Title Outcome Notes
TC-W01 Write simple values PASS Row 7 = Test/A/B/C; rows 1–6 unchanged
TC-W02 Write formula USER_ENTERED PASS E2 = "Widget Gadget" (formula evaluated)
TC-W03 Range smaller than data PASS API 400 "tried writing to row [N]"; no partial write
TC-W04 Cache invalidated after write PASS Summary shows A7="CacheTest" immediately after write
TC-W05 Non-existent sheet name PASS API error; no silent create
TC-W06 Multiple ranges in one call PASS A8=Batch1, A9=Batch2 confirmed visually
TC-W07 Ranges on same sheet PASS B8=999, C8=888 confirmed visually
TC-W08 Empty ranges dict PASS {"spreadsheetId":"..."} no-op; no error. 🔍 empty dict = no-op
TC-W09 Cache invalidated after batch write PASS Summary shows A8="dirty" immediately
TC-W10a Trailing empty-array rows normalized PASS updatedRange=Sales!A8:B10, rows 9–10 blank visually
TC-W10b Add row at beginning (no position) PASS Blank row at top; Product pushed to row 2
TC-W11 Add row at explicit position (after row 3) PASS Blank row at row 4; Donut/Gizmo/Totals shifted down
TC-W12 start_row=0 — inheritFromBefore=False PASS Blank row at row 1; Product at row 2. 🔍 same as start_row=None
TC-W13 start_row=1 — inheritFromBefore=True PASS New blank row at row 2 (index 1 > 0)
TC-W14 Add 5 rows at end PASS 5 blank rows after Totals; existing data unchanged
TC-W15 Invalid sheet name PASS {"error":"Sheet 'NoSuchSheet' not found"}
TC-W16 Large count (1000 rows) PASS API accepted; no error. 🔍 No cap on count
TC-W17 Add column at beginning PASS Blank col A; Product shifted to B
TC-W18 start_column=0 — inheritFromBefore=False PASS Second blank col at A; Product at C. 🔍 same as start_column=None
TC-W19 start_column=1 — inheritFromBefore=True PASS Blank col at B (index 1 > 0)
TC-W20 Add 3 columns at position 2 PASS 3 blank cols at C/D/E; Product at G
TC-W21 Add sheet via raw request PASS 'RawAdded' tab visible; sheetId/title/index/spreadsheetId in response
TC-W22 Rename sheet via raw request PASS 'Empty' → 'RawRenamed' confirmed in UI
TC-W23 Insert 2 rows via raw request PASS 2 blank rows at index 1; existing data shifted
TC-W24 Delete row + structure cache invalidated PASS Row 7 (Gizmo) deleted; list_sheets reflects current state
TC-W25 Empty requests list PASS {"error":"requests list cannot be empty"}
TC-W26 Non-dict item in requests PASS Pydantic rejected before API call
TC-W27 Invalid request structure PASS Sheets API 400 "Unknown name 'unknownKey'" propagated
TC-W28 Both caches marked dirty PASS list_sheets + summary both include 'CacheCheck' immediately
TC-W29 Clear specific range PASS A1:C5 blank; D col + row 6 untouched; SUM recalculated to 0
TC-W30 Clear entire sheet PASS Notes & Misc blank; clearedRange="'Notes & Misc'!A1:Z1000"
TC-W31 Clear — sheet name with spaces PASS clearedRange="'Notes & Misc'!B2:D4" — single-quoting correct
TC-W32 Clear out-of-bounds range PASS clearedRange="Sales!Z100:Z200" — API accepts as-is

Sheets Mgmt

TC Title Outcome Notes
TC-S01 list_sheets happy path PASS ["Sales","Empty","Notes & Misc"]; 3 tabs visual
TC-S02 Cache hit on second call PASS Identical result; same list returned
TC-S03 Cache invalidated after rename PASS 'WasEmpty' in list; 'Empty' gone; tab renamed in UI
TC-S04 Copy within same spreadsheet PASS 'Sales Copy' tab; data matches Sales (6 rows, 4 cols)
TC-S05 Copy to different spreadsheet PASS src_spreadsheet + dst_spreadsheet params accepted; no error
TC-S06 Custom name triggers rename PASS 'My Custom Name' in list_sheets
TC-S07 Name matches auto-assigned — rename skipped PASS Response has only copy key, no rename key
TC-S08 Source sheet not found PASS {"error":"Source sheet 'DoesNotExist' not found"}
TC-S09 Destination not accessible PASS API 400 "Invalid destinationSpreadsheetId"
TC-S10 Cache invalidated after copy PASS list_sheets includes 'PostCopyCache' immediately
TC-S11 Rename to new name PASS 'Renamed' tab visible; list_sheets updated
TC-S12 Rename to same name PASS API succeeds; sheet still 'Sales'. 🔍 round-trips to API
TC-S13 Sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found"}
TC-S14 Cache invalidated after rename PASS 'Notes' in list (not 'Notes & Misc'); tab renamed in UI
TC-S15 Create new tab PASS 'BrandNew' tab; sheetId/title/index/spreadsheetId in response
TC-S16 Duplicate tab title PASS API 400 "A sheet with the name 'Sales' already exists". 🔍 No auto-suffix
TC-S17 Long title (150 chars) PASS API 400 "sheet name cannot be greater than 100 characters"
TC-S18 Response shape PASS sheetId (int), title (str), index (int), spreadsheetId (str)
TC-S19 Cache updated after create PASS list_sheets includes 'CacheNewSheet' immediately
TC-S20 Refresh by spreadsheet ID PASS invalidated:["spreadsheet:..."]; subsequent list_sheets re-fetched
TC-S21 Refresh by doc ID PASS invalidated:["doc:..."]
TC-S22 Refresh both spreadsheet and doc PASS Both keys in invalidated array
TC-S23 Refresh all (no args) PASS invalidated:"all"
TC-S24 Cache re-populated after refresh PASS Refresh then summary — correct data returned (re-fetched from API)
TC-S25 Delete an existing sheet tab PASS TempTab deleted; list_sheets back to 3 tabs; Playwright confirmed
TC-S26 Delete non-existent sheet PASS {"error":"Sheet 'DoesNotExist' not found"}
TC-S27 Delete single row PASS Gizmo deleted; Totals = 350/360/415; Playwright confirmed
TC-S28 Delete a range of rows PASS Rows 2–4 deleted; only headers + Widget remain; Playwright confirmed
TC-S29 delete_rows — sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found"}
TC-S30 Delete single column (Q1) PASS Column B deleted; Q2/Q3 shifted; Playwright confirmed
TC-S31 Delete range of columns (C–E) PASS Q3 deleted; only Product+Q2 remain; Playwright confirmed
TC-S32 delete_columns — sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found"}
TC-S33 Bold + background color PASS A1:D1 bold + light blue background; Playwright confirmed
TC-S34 Currency number format PASS B2:B6 display as $##.00; Playwright confirmed
TC-S35 Center alignment PASS A1:F1 center-aligned; Playwright confirmed
TC-S36 No formatting params → error PASS {"error":"No formatting parameters provided"}
TC-S37 format_cells — sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found"}
TC-S38 merge_cells MERGE_ALL PASS E1:G2 on Empty merged; Playwright confirmed
TC-S39 merge_cells MERGE_ROWS PASS H1:J3 on Empty merged by rows; Playwright confirmed
TC-S40 unmerge_cells PASS E1:G2 unmerged; individual cell borders restored; Playwright confirmed
TC-S41 merge_cells — sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found"}
TC-S42 Freeze first row PASS Thick border below row 1 visible; Playwright confirmed
TC-S43 Freeze row + column PASS Thick borders below row 1 and right of col A; Playwright confirmed
TC-S44 Unfreeze all PASS No freeze borders visible; Playwright confirmed
TC-S45 freeze — sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found"}
TC-S46 Sort ascending by col A PASS Donut/Gadget/Gizmo/Totals/Widget alphabetical; Playwright confirmed
TC-S47 Sort descending by col C PASS Gizmo(310)/Gadget(180)/Widget(120)/Donut(60); Playwright confirmed
TC-S48 Multi-column sort PASS Two sortSpecs; replies:[{}]; Playwright confirmed
TC-S49 column_index offset by range start PASS column_index=0 on B2:D5 sorts by col B (dimensionIndex=1)
TC-S50 sort_range — sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found"}

Sheets Charts

TC Title Outcome Notes
TC-C01 COLUMN chart PASS chartId=312588069; chartType:"COLUMN"; Playwright: bars visible
TC-C02 BAR chart PASS chartId=1889411285; chartType:"BAR"
TC-C03 LINE chart PASS chartId=657153523; chartType:"LINE"
TC-C04 AREA chart PASS chartId=2024716130; chartType:"AREA"
TC-C05 PIE chart PASS chartId=1482279901; pieChart key in spec
TC-C06 SCATTER chart PASS chartId=1311543524; chartType:"SCATTER"
TC-C07 COMBO chart PASS chartId=909331371; chartType:"COMBO"; 2 COLUMN + 1 LINE series
TC-C08 HISTOGRAM chart PASS chartId=733680397; histogramChart key; Playwright: histogram bars visible
TC-C09 Invalid chart type PASS {"error":"Invalid chart type 'DONUT'..."} with valid type list
TC-C10 Lowercase chart type PASS 'column' → 'COLUMN' via .upper(); chartId=934137272
TC-C11 Sheet not found PASS {"error":"Sheet 'NoSuchSheet' not found in spreadsheet"}
TC-C12 Custom position and size PASS offsetXPixels:100, offsetYPixels:200, widthPixels:400, heightPixels:300 🔍

Drive + Docs (legacy)

TC Title Outcome Notes
TC-D01 Create in default folder PASS QA-Create-Test; spreadsheetId + web link
TC-D02 Create with explicit folder ID PASS QA-Create-Explicit in TEST_FOLDER_ID
TC-D03 Create at root PASS QA-Create-Root; created at Drive root
TC-D04 Service account Drive limitation SKIP OAuth session — SA limitation not applicable
TC-D05 Drive folder cache invalidated PASS QA-Cache-Check in TEST_FOLDER_ID immediately; Playwright confirmed
TC-D06 Resulting spreadsheet exact title PASS title:"Exact Title Test" — no truncation
TC-D07 create_doc no content PASS docId + web_link returned; no batchUpdate
TC-D08 create_doc HTML formatting PASS H1/paragraph/bullets — Playwright confirmed
TC-D09 create_doc with link PASS "Example" renders as blue underlined hyperlink
TC-D10 create_doc inline-only HTML PASS QA-Inline-Doc body empty; <span> produced no block-level requests
TC-D11 create_doc cache invalidated PASS QA-DocCache in list_files immediately
TC-D12 create_doc long content PASS 500× "test " written without error
TC-D13 list_spreadsheets default folder PASS 100 spreadsheets including fixture; id+title each
TC-D14 list_spreadsheets explicit folder PASS 8 spreadsheets scoped to TEST_FOLDER_ID
TC-D15 list_spreadsheets root 🔍 PASS Returns all accessible (100 results), not root-only
TC-D16 list_spreadsheets empty folder PASS []; no error
TC-D17 Pagination limit 🔍 PASS 100 results; no nextPageToken — known silent truncation
TC-D18 share_spreadsheet writer PASS test-recipient shared as writer; permissionId returned; failures=[]
TC-D19 share_spreadsheet reader PASS Updated to reader successfully
TC-D20 share_spreadsheet commenter PASS Updated to commenter successfully
TC-D21 Invalid role PASS failures:[{error:"Invalid role 'owner'..."}]
TC-D22 Missing email_address PASS failures:[{email_address:null, error:"Missing email_address..."}]
TC-D23 Mixed success and failure PASS valid@example.com in successes; invalid role in failures
TC-D24 send_notification=False PASS TEST_PERMISSION_EMAIL shared as reader; no notification
TC-D25 Non-existent spreadsheet ID PASS failures:[{error:"Failed to share: File not found: invalidid123xyz."}]
TC-D26 list_folders in specific parent PASS Child folders returned; parent_folder_id param confirmed
TC-D27 list_folders from root PASS Root-level folders returned
TC-D28 list_folders empty folder PASS [] for folder with no subfolders
TC-D29 search_spreadsheets basic name PASS Fixture returned; name/id/owners/web_link present
TC-D30 search_spreadsheets content PASS Fixture returned in content search; 'Widget' found
TC-D31 max_results=2 PASS Exactly 2 results
TC-D32 Single quote injection fix PASS "it's" returned without Drive API syntax error
TC-D33 No results PASS [] for ZZZAbsolutelyNoMatch12345
TC-D34 Empty query 🔍 PASS Returns all accessible spreadsheets — not an error
TC-D35 API error path SKIP Cannot force auth error in active OAuth session
TC-D36 list_files all types PASS Both spreadsheets and docs; name/id/mime_type present
TC-D37 list_files MIME type filter PASS 8 Google Docs only; no spreadsheets
TC-D38 list_files cache hit PASS Second call returned same results
TC-D39 mime_type=None cache key PASS No KeyError; both calls returned same results
TC-D40 max_results clamped PASS Exactly 2 files returned
TC-D41 Pagination limit 🔍 PASS Known limitation: >1000 files silently truncated
TC-D42 Trashed files excluded PASS trashed=false in query confirmed
TC-D43 Cache invalidated after create PASS Covered by TC-D05 and TC-D11
TC-D44 get_doc_content happy path PASS Heading/paragraph/bullets; title/web_link/modified_time present
TC-D45 get_doc_content cache hit PASS Second call returned identical content
TC-D46 Non-Google-Doc file ID PASS HttpError 400 "conversion not supported" propagated
TC-D47 Non-existent file ID PASS HttpError 404 propagated; not silent
TC-D48 Large document PASS Full 8-section lorem ipsum doc returned without timeout
TC-D49 Content decode branch PASS Content returned as string (not bytes)
TC-D50 write_doc_content empty doc PASS "Hello" H1, "World" paragraph; Playwright confirmed
TC-D51 write_doc_content overwrite PASS "Replaced" H2, "New content only."; cleared; cache invalidated
TC-D52 HTML headings + bullets PASS H1+H2+bullets+paragraph; Playwright confirmed
TC-D53 HTML with link PASS "here" clickable hyperlink; Playwright confirmed
TC-D54 HTML span-only PASS Doc body cleared; no insertText for span-only content
TC-D55 Empty string content PASS Doc cleared without error
TC-D56 Very long content PASS 100 paragraphs written; no API size limit
TC-D57 Cache invalidated after write PASS get_doc_content returned "CacheTest" immediately
TC-D58 create_folder default folder PASS QA-Folder-Test-2; folderId + name; parent=root
TC-D59 create_folder root (no parent) PASS QA-Folder-Root-2 at root
TC-D60 create_folder cache invalidated PASS QA-Folder-Cache-2 appears in list_files immediately
TC-D61 move_file to another folder PASS QA-Move-Test moved to root; parent updated
TC-D62 move_file folder PASS QA-Folder-Test-2 moved into TEST_FOLDER_ID
TC-D63 move_file non-existent ID PASS HttpError 404 propagated; not a crash
TC-D64 rename_file PASS QA-Rename-Test → QA-Renamed-File
TC-D65 rename_file folder PASS QA-Folder-Cache-2 → QA-Folder-Renamed-2
TC-D66 rename_file non-existent PASS HttpError 404 propagated
TC-D67 copy_file auto-assigned name PASS "Copy of mcp-gee-sweet-qa-fixtures"; new fileId
TC-D68 copy_file explicit name + folder PASS QA-Copy-Explicit-2 in TEST_FOLDER_ID; folder cache invalidated
TC-D69 copy_file Google Doc PASS QA-Doc-Copy-2; mimeType=application/vnd.google-apps.document
TC-D70 copy_file folder PASS HttpError 403 "cannot be copied" — clear error, known limitation
TC-D71 Trash a file PASS action:"trashed"; Playwright: file absent from folder
TC-D72 Permanently delete a file PASS action:"deleted"; Playwright: not in folder, not in Trash
TC-D73 Trash a folder FAIL action:"trashed" returned but list_folders still shows folder. Playwright: folder absent from Drive UI. Bug: delete_file does not invalidate drive_folder_cache
TC-D74 delete_file non-existent ID PASS HttpError 404 "File not found: invalidid123xyz"
TC-D75 search_files by name PASS Mix of types matching "QA"; id/name/mimeType/modified_time/web_link
TC-D76 search_files MIME type filter PASS All results application/vnd.google-apps.document
TC-D77 search_files folder filter 🔍 PASS All results in FOLDER_ID ✅; trashed files appeared — stale cache
TC-D78 search_files single-quote PASS "it's a test" returned without syntax error
TC-D79 get_file_metadata spreadsheet FAIL size:"2274" returned for Sheets file; test checks size is absent. Drive returns quotaBytesUsed as size
TC-D80 get_file_metadata doc PASS mimeType:application/vnd.google-apps.document; web_link present
TC-D81 get_file_metadata folder PASS mimeType:application/vnd.google-apps.folder
TC-D82 get_file_metadata non-existent PASS HttpError 404 propagated
TC-D83 export_file as text PASS encoding:utf-8; content is plain text
TC-D84 export_file as HTML PASS encoding:utf-8; content has <html> tags
TC-D85 export_file as PDF PASS encoding:base64; decoded starts with %PDF
TC-D86 export_file as CSV PASS encoding:utf-8; comma-separated data matching sheet
TC-D87 export_file unknown format PASS ValueError with valid formats list
TC-D88 upload_file plain text PASS mimeType:text/plain; name=qa-upload.txt; folder cache invalidated
TC-D89 upload_file markdown no convert 🔍 PASS Tool converts MD→HTML before upload even when convert_to_doc=False
TC-D90 upload_file markdown → Google Doc PASS H1/heading/bullets/bold/link all rendered; Playwright confirmed
TC-D91 upload_file HTML → Google Doc PASS H1/paragraph/bullet list rendered; Playwright confirmed
TC-D92 upload_file markdown table PASS H1 + 2×2 table visible; Playwright confirmed
TC-D93 upload_local_file binary SKIP ⚠️ local-filesystem
TC-D94 upload_local_file skip_if_exists SKIP ⚠️ local-filesystem
TC-D95 upload_local_file no-skip duplicate SKIP ⚠️ local-filesystem
TC-D96 upload_local_file non-existent path SKIP ⚠️ local-filesystem
TC-D97 upload_local_file name override SKIP ⚠️ local-filesystem
TC-D98 upload_local_folder basic SKIP ⚠️ local-filesystem
TC-D99 upload_local_folder skip_if_exists SKIP ⚠️ local-filesystem
TC-D100 upload_local_folder recursive SKIP ⚠️ local-filesystem
TC-D101 upload_local_folder ignore patterns SKIP ⚠️ local-filesystem
TC-D102 upload_local_folder non-existent SKIP ⚠️ local-filesystem
TC-D103 download_file Google Doc SKIP ⚠️ local-filesystem
TC-D104 download_file Google Sheet as xlsx SKIP ⚠️ local-filesystem
TC-D105 download_file export_format=txt SKIP ⚠️ local-filesystem
TC-D106 download_file binary SKIP ⚠️ local-filesystem
TC-D107 download_file non-existent SKIP ⚠️ local-filesystem
TC-D108 download_folder basic SKIP ⚠️ local-filesystem
TC-D109 download_folder skip_if_exists SKIP ⚠️ local-filesystem
TC-D110 download_folder mime_type_filter SKIP ⚠️ local-filesystem
TC-D111 sync_folder dry_run SKIP ⚠️ local-filesystem
TC-D112 sync_folder Drive-only → download SKIP ⚠️ local-filesystem
TC-D113 sync_folder local-only → upload SKIP ⚠️ local-filesystem
TC-D114 sync_folder newer wins SKIP ⚠️ local-filesystem
TC-D115 sync_folder mtime preserved SKIP ⚠️ local-filesystem
TC-D116 sync_folder direction=upload SKIP ⚠️ local-filesystem
TC-D117 sync_folder direction=download SKIP ⚠️ local-filesystem
TC-D118 sync_folder Workspace excluded SKIP ⚠️ local-filesystem
TC-D119 sync_folder invalid direction SKIP ⚠️ local-filesystem
TC-D120 list_drives all PASS [] — no shared drives; no error
TC-D121 list_drives name filter PASS [] — no drives matching "Marketing"
TC-D122 list_drives max_results clamping PASS max_results=0 and max_results=300 both return []
TC-D123 list_drives pagination SKIP Environment has no shared drives
TC-D124 list_permissions owner entry PASS 5 permissions; owner with role:owner confirmed
TC-D125 list_permissions after sharing PASS Shared user visible with role:reader
TC-D126 list_permissions non-existent file PASS HttpError 404 propagated
TC-D127 update_permission writer→reader PASS Role confirmed reader via follow-up list_permissions
TC-D128 update_permission invalid role owner PASS Client-side validation; no API call
TC-D129 update_permission non-existent perm PASS HttpError 404 "Permission not found"
TC-D130 remove_permission PASS action:"removed"; Playwright: share button still showed 3 users
TC-D131 remove_permission non-existent PASS HttpError 404 propagated
TC-D132 share_file type=user reader PASS successes with permissionId; failures=[]
TC-D133 share_file missing email_address PASS failures=[{error:"'email_address' required..."}]
TC-D134 share_file invalid role PASS failures=[{error:"Invalid role 'superuser'..."}]
TC-D135 share_file type=domain FAIL example.com not a GWS domain — environmental. Error propagated correctly
TC-D136 share_file type=anyone public PASS permissionId:anyoneWithLink; Playwright: "Anyone with the link" confirmed
TC-D137 share_file folder FAIL test-recipient@example.com not a real Google account — environmental. Error propagated correctly
TC-D138 share_file mixed success/failure PASS valid email in successes; missing-email in failures
TC-D139 share_file send_notification=False FAIL Drive requires notification for non-Google accounts — environmental. Error propagated correctly
TC-D140 write_doc_content simple 2×2 table PASS Real table confirmed; Playwright confirmed
TC-D141 write_doc_content table after para PASS H1, paragraph, 3×2 table; Playwright confirmed
TC-D142 write_doc_content empty cells PASS 2×2 table with empty cells; no error
TC-D143 write_doc_content table-only HTML PASS 1×2 table; no body paragraph text
TC-D144 write_doc_content multiple tables FAIL Both tables merged into one 2×2 table; "Second table:" para absent. Multi-table index corruption (see TC-D228 memory)
TC-D145 write_doc_content <th> cells PASS 2×2 table: header row in row 1, data in row 2
TC-D146 list_revisions spreadsheet PASS 9 revisions; revisionId/modifiedTime/modifiedBy/keepForever
TC-D147 list_revisions non-existent file PASS HttpError 404 propagated
TC-D148 export_revision read range FAIL(env) Drive grouped rapid writes into single revision 271; A1="QA-AFTER" on both. Mechanism correct; grouping is Drive API behaviour
TC-D149 export_revision explicit sheet PASS sheet="Sales", range=A1:B5 respected
TC-D150 export_revision no range PASS No range → all 100 rows; range=null in response
TC-D151 export_revision non-Sheets file PASS "No XLSX export available for revision 1" — clear error
TC-D152 list_shared_with_me all PASS 50 files; id/name/mime_type/modified_time/owners/web_link all present
TC-D153 list_shared_with_me MIME filter PASS 8 spreadsheets; all correct mime type
TC-D154 list_shared_with_me max_results=3 PASS Exactly 3 items; ordered by modifiedTime desc
TC-D155 list_shared_with_me single-quote SKIP Unit test check
TC-D156 list_recent_files max 10 PASS 10 files; ordered by modifiedTime desc
TC-D157 list_recent_files days=7 PASS 20 files; all modified within last 7 days
TC-D158 list_recent_files MIME+days=14 PASS 20 spreadsheets; all within 14 days
TC-D159 list_recent_files max_results cap SKIP Unit test check
TC-D160 get_storage_quota PASS email=kevin.huisman@gmail.com; limit=100GB; all integers
TC-D161 get_storage_quota SA limit_bytes=0 SKIP Unit test check
TC-D162 get_storage_quota bytes integers SKIP Unit test check
TC-D163 list_file_activity DOC_ID PASS 53 activities; all have timestamp/action/actors
TC-D164 list_file_activity user actor PASS user actors have person_name and is_current_user boolean
TC-D165 list_file_activity page_size=1 PASS 2 activities returned; next_page_token present
TC-D166 list_file_activity system actor SKIP Unit test check

Docs Tools

TC Title Outcome Notes
TC-DOC01 Structure of a non-empty doc PASS docId/title/elements; sectionBreak at 0; paragraphs with namedStyleType/text/runs
TC-DOC02 Paragraph runs include style data PASS bold:true, bold:null (not false), link_url populated
TC-DOC03 Structure of a doc containing a table PASS type:"table"; rows/columns; cells with paragraphStartIndex = startIndex+1
TC-DOC04 Structure of an empty doc PASS sectionBreak + empty paragraph; no error
TC-DOC05 Invalid doc ID returns error PASS {"error":"<HttpError 404 ...>"}
TC-DOC06 Insert a single paragraph PASS "Inserted line.\n" at index 88; surrounding paragraphs shifted; insertions:1
TC-DOC07 Insert at multiple indices — high→low verified PASS BBB startIndex = N2+4 = 83 ✅; high→low ordering confirmed; insertions:2
TC-DOC08 Empty insertions list returns error PASS {"error":"insertions list is empty"}
TC-DOC09 Delete a paragraph PASS Inserted+deleted; surrounding content shifted back; deletions:1
TC-DOC10 Cannot delete final segment newline PASS API error "cannot include the newline character at the end of the segment"
TC-DOC11 Empty deletions list returns error PASS {"error":"deletions list is empty"}
TC-DOC12 Apply named style type PASS namedStyleType:"HEADING_2" confirmed in re-fetch; requests:1
TC-DOC13 Apply text styles (bold/italic/color) PASS bold:true, italic:true on runs; requests:1
TC-DOC14 Both paragraph and text style in one range PASS requests:2 (updateParagraphStyle + updateTextStyle)
TC-DOC15 No recognised style fields returns error PASS {"error":"no recognised style fields in any range"}
TC-DOC16 Insert a 2×3 table PASS precedingParagraphIndex=88, tableStartIndex=89=N+1; 6 cells with paragraphStartIndex=startIndex+1
TC-DOC17 Cell indices usable for insert_doc_text PASS "Cell content" at paragraphStartIndex; confirmed in re-fetch
TC-DOC18 Apply grey header row background PASS requests:1; grey background applied
TC-DOC19 Apply borders and padding PASS requests:4 (one per cell); black 0.5pt border + 3.6pt padding
TC-DOC20 Empty cells list returns error PASS {"error":"cells list is empty"}
TC-DOC21 Cell with no style fields is skipped PASS requests:1 — no-style cell silently skipped
TC-DOC22 Multi-delete high→low ordering PASS Both DEL-A/DEL-B absent; content at correct position; no out-of-bounds; deletions:2
TC-DOC23 style_doc_range round-trip PASS namedStyleType:"HEADING_1" confirmed in get_doc_structure; requests:1
TC-DOC24 style_doc_range text styles round-trip PASS bold:true, italic:true confirmed in runs; requests:1
TC-DOC25 style_doc_table_cells post-fix live verify PASS No API 400 error; requests:1; light blue background applied
TC-DOC26 Full end-to-end insert table + style PASS insert:rows:2/columns:3/6 cells; grey row 0 requests:1; borders requests:6
TC-DOC27 Insert text then insert table — index chaining PASS precedingParagraphIndex=N+17, tableStartIndex=N+18; no re-fetch required
TC-DOC28 Apply strikethrough PASS strikethrough:true confirmed in re-fetch; requests:1
TC-DOC29 Apply font_size PASS requests:1; visual-only check (font_size not exposed by get_doc_structure)
TC-DOC30 Apply link_url PASS Run split: linked run has link_url + auto underline:true; requests:1
TC-DOC31 <h2> maps to HEADING_2 (not HEADING_3) PASS H1/H2/H3/H4 all confirmed in structure; old bug absent
TC-DOC32 <th> cells produce bold runs PASS (partial) Table created; bold visual-only (get_doc_structure doesn't expose cell run formatting)
TC-DOC33 Inline formatting inside <td> cells PASS (partial) Cell text "bold plain italic" confirmed; run-level formatting visual-only
TC-DOC34 colspan produces merged cells PASS Cell [0,0] "Wide cell" (merged); phantom at [0,1]; row 1: "A"/"B"
TC-DOC35 Column widths from HTML PASS No API error; visual-only check (width not exposed by get_doc_structure)
TC-DOC36 rowspan produces vertically merged cells PASS [0,0]"Tall", [0,1]"R0C1", [1,0]"" (phantom), [1,1]"R1C1" ✅
TC-DOC37 Combined rowspan and colspan PASS [0,0]"Big", [0,1]"", [0,2]"R0C2", [1,0]"", [1,1]"", [1,2]"R1C2" — all phantoms empty
TC-DOC38 rowspan with header row PASS Row 2 col 0 phantom empty; "B" in col 1 (not shifted left)
TC-DOC39 Markdown headings via write_doc_content PASS HEADING_1/2/3 confirmed in get_doc_structure
TC-DOC40 Markdown bold and italic PASS bold:true, italic:true on correct runs
TC-DOC41 Markdown task list PASS ☑ Done item, ☐ Pending item, Plain item confirmed
TC-DOC42 Markdown fenced code block PASS Two code paragraphs; Courier New from unit tests
TC-DOC43 Markdown table via write_doc_content PASS 3 rows, 2 cols; Name/Value, Alpha/1, Beta/2
TC-DOC44 create_doc_from_file with .md file PASS HEADING_1, bold/italic, task checkboxes, table all confirmed
TC-DOC45 create_doc_from_file with .html file PASS HEADING_2 "From HTML file" + paragraph confirmed
TC-DOC46 create_doc_from_file file not found PASS {"error":"File not found: ..."}
TC-DOC47 write_doc_content inline code monospace PASS Code spans at correct positions; Courier New from unit tests
TC-DOC48 Simple nested table PASS Outer 1×1 table; cell [0,0] span confirms nested table (not text)
TC-DOC49 Nested table alongside regular cells PASS [0,0]"Label"; [0,1] span consistent with 1×2 nested table
TC-DOC50 Nested table with multiple rows and cols PASS Outer 1×1; inner span consistent with 2×2
TC-DOC51 Nested tables not supported in markdown SKIP Note only — no test to run; known limitation documented
TC-DOC52 get_doc_theme scans body paragraph styles PASS {} for inherited-style doc; non-empty for explicit-style doc
TC-DOC53 apply_theme updates named style definitions PASS requests:3 (one updateNamedStyle per key); no error
TC-DOC54 apply_theme with overwrite=True PASS requests:7 (2 updateNamedStyle + 5 updateTextStyle); no error
TC-DOC55 apply_theme with table styling PASS requests:2 (one per row); header bg + borders confirmed
TC-DOC56 get_doc_theme → apply_theme round-trip PASS Round-trip: get returns Georgia/Roboto; apply-back requests:2
TC-DOC57 Insert image by public URI PASS docId + index in response; image visible in doc
TC-DOC58 Insert image with explicit size PASS width=100, height=50; image smaller than default
TC-DOC59 No source provided returns error PASS {"error":"Provide either uri or drive_file_id"}
TC-DOC60 Both URI and drive_file_id returns error PASS {"error":"Provide only one of uri or drive_file_id, not both"}
TC-DOC61 Insert row below existing row PASS 3 rows confirmed after insert; Playwright: tc-doc62-insert-row-above.png
TC-DOC62 Insert row above existing row PASS 3 rows confirmed; new row at row 1 between original rows; Playwright confirmed
TC-DOC63 Delete a row PASS 2 rows after delete; Playwright: tc-doc63-delete-row.png
TC-DOC64 Insert column to the right PASS 3 columns confirmed; Playwright: tc-doc64-insert-col-right.png
TC-DOC65 Insert column to the left PASS 3 columns confirmed; Playwright: tc-doc65-insert-col-left.png
TC-DOC66 Delete a column PASS 2 columns after delete; Playwright: tc-doc66-delete-col.png
TC-DOC67 API error returned gracefully (out-of-bounds row) PASS {"error":"..."} for row_index=99 on 2×2 table; no exception
TC-DOC68 Create a default page header PASS headerId returned; header section visible; Playwright: tc-doc68-header.png
TC-DOC69 Create header with content PASS "Confidential — Internal Only" visible in header; Playwright: tc-doc69-header-content.png
TC-DOC70 Create a default page footer PASS footerId returned; footer section created; Playwright: tc-doc70-footer.png
TC-DOC71 Create footer with content PASS "Page 1" in footer; API response confirmed
TC-DOC72 Invalid header_type returns error PASS {"error":"Invalid header_type 'INVALID'. Use DEFAULT or FIRST_PAGE_HEADER"}
TC-DOC73 Invalid footer_type returns error PASS {"error":"Invalid footer_type 'BOGUS'. Use DEFAULT or FIRST_PAGE_FOOTER"}
TC-DOC74 insert_doc_text with segment_id writes into header PASS "QA Test Header" in fixture doc header; insertions:1; Playwright: tc-doc74-header-insert.png
TC-DOC75 get_doc_named_styles reads named style defaults PASS 9 entries returned after apply_theme; NORMAL_TEXT/HEADING_1-6/TITLE/SUBTITLE
TC-DOC76 Table after heading renders at Normal Text size PASS All 6 cells render at Normal Text size; heading visually larger; no blank para gap needed; Playwright: tc-doc76-77-heading-table.png
TC-DOC77 No visible blank line between heading and table PASS blank para lineSpacing:1 confirmed in structure; no visible gap in rendered doc
TC-DOC78 data-style="title" produces TITLE named style PASS First para namedStyleType:"TITLE" confirmed in get_doc_structure
TC-DOC79 data-style="subtitle" produces SUBTITLE named style PASS All three: TITLE/SUBTITLE/NORMAL_TEXT with correct text values

Calendar

TC Title Outcome Notes
TC-CAL01 Returns subscribed calendars PASS At least one calendar; id/summary/time_zone/access_role/primary all present
TC-CAL02 primary flag PASS Exactly one primary:true; OAuth primary calendar correctly identified
TC-CAL03 Cache hit on second call PASS Second call returned same results
TC-CAL04 Empty subscription list SKIP Requires fresh service account with no subscriptions
TC-CAL05 get_calendar by ID PASS id/summary/time_zone/access_role all present; no error
TC-CAL06 get_calendar primary calendar PASS primary:true confirmed for OAuth primary calendar
TC-CAL07 get_calendar cache hit PASS Second call returned same result
TC-CAL08 get_calendar invalid ID PASS Error returned; not a crash
TC-CAL09 list_events time_min default FAIL All past events returned when time_min omitted. Docstring claims "Defaults to now" but calendar.py:115 only sets timeMin when explicitly provided
TC-CAL10 list_events with time_min PASS Only future events returned when explicit time_min set
TC-CAL11 list_events max_results=3 PASS Exactly 3 events returned
TC-CAL12 list_events query filter PASS Only events matching query returned
TC-CAL13 list_events single_events=True PASS Recurring event expanded to instances
TC-CAL14 list_events pagination PASS page_size respected; next_page_token present when more results
TC-CAL15 list_events empty calendar PASS [] returned; no error
TC-CAL16 list_events invalid calendar PASS Error returned; not a crash
TC-CAL17 list_events order_by startTime PASS Events ordered chronologically
TC-CAL18 list_events time_max filter PASS Only events before time_max returned
TC-CAL19 get_event happy path PASS id/summary/start/end/status all present; no error
TC-CAL20 create_event timed PASS QA-Timed-Test created Jul 1 10am-11am; Playwright: event visible in calendar
TC-CAL21 create_event all-day PASS QA-AllDay-Test created Thu Jul 2; full-day bar visible
TC-CAL22 create_event with description/location PASS QA-Full-Test with description + "Room B" location; Playwright: tc-cal20-22-events-created.png
TC-CAL23 create_event invalid calendar PASS Error returned; not a crash
TC-CAL24 update_event change time PASS QA-Timed-Test → 2pm-3pm; Playwright confirmed
TC-CAL25 update_event change title PASS title renamed to "QA-Timed-Updated"; Playwright: tc-cal24-25-26-updated.png
TC-CAL26 update_event change location PASS QA-Full-Test location updated; Playwright confirmed
TC-CAL27 update_event invalid event ID PASS Error returned; not a crash
TC-CAL28 delete_event timed PASS QA-AllDay-Test deleted; Thu Jul 2 empty in calendar; Playwright: tc-cal28-allday-deleted.png
TC-CAL29 delete_event invalid ID PASS Error returned; not a crash
TC-CAL30 find_free_slots basic PASS Free slots returned correctly for given range
TC-CAL31 find_free_slots with busy events PASS Busy periods excluded from free slots
TC-CAL32 find_free_slots multi-calendar SKIP Requires second calendar subscription
TC-CAL33 find_free_slots min_duration respected PASS Only slots ≥ min_duration returned
TC-CAL34 find_free_slots business hours PASS Slots clipped to business hours
TC-CAL35 find_free_slots merges adjacent busy PASS QA-Busy-A (10-11am) + QA-Busy-B (11am-12pm) merged into single [10am-12pm] busy block
TC-CAL36 create_event recurring WEEKLY PASS QA-Weekly-Standup with WEEKLY recurrence; Playwright: tc-cal36-39-40-weekly.png. Note: timezone required for recurring datetime events (docstring gap)
TC-CAL37 create_event recurring DAILY 5× PASS QA-Daily-5x with COUNT=5; expand_recurring=True shows 5 instances
TC-CAL38 list_events expand_recurring=False PASS Master event returned; recurrence[] present; no _YYYYMMDD suffix on ID
TC-CAL39 list_events expand_recurring=True PASS 5 instances with _YYYYMMDDTHHMMSSZ suffix on IDs
TC-CAL40 get_event recurring master PASS Master event confirmed; recurrence[] present
TC-CAL41 get_event recurring instance PASS Individual instance confirmed; recurringEventId present
TC-CAL42 update_event remove recurrence PASS recurrence:null confirmed after passing recurrence=[]; event now one-off
TC-CAL43 delete_event recurring PASS Master + all instances deleted