test: improve unit test coverage for core modules#80
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use Connectable.fromString() instead of private constructor. Assert specific op types (add/remove/replace) instead of just checking that diffs exist. Add coverage for removals, modifications, and constraint changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…maxRows, and error paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rage Cover constructor derived booleans (isTargetlessSelectQuery only true for SELECTs), data field copying, withOptimization mutation, analyze integration, and edge cases for static helpers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Found New Recommended Indexes
The following indexes are likely to make the most impact to your queries. They're ordered by how many queries seen in your tests
| Index Definition | Usage Count |
|---|---|
assets(event_id, uploader_id, inserted_at desc) |
1 |
guest_ip_addresses(ip_address) |
1 |
Statistics Mode
When generating recommendations, we made the following changes to your database statistics:
fixed rows per table
- Rows per table: 10000
- Pages per table: 1
Instead of assuming a fixed number of rows per table (which can cause unnecessary recommendations), you can export statistics from your production database and import it using the STATISTICS_PATH environment variable. You can read more about how to sync stats here.
- name: Analyze
uses: query-doctor/analyzer@v0
env:
STATISTICS_PATH: ./statistics.jsonOptimization Overview
| Query | Base Cost | Optimized Cost | Improvement |
|---|---|---|---|
| 30f59ed4d6cab15b | 15922.03 | 1638.53 | 9.72x |
| 62cddf9e2b2dad50 | 126 | 9.04 | 13.94x |
Query 30f59ed4d6cab15b
New indexes improve cost by 9.72x:assets(event_id, uploader_id, inserted_at desc)
View Query (too long to display inline)
SELECT
"guests"."id",
"guests"."session_id",
"guests"."username",
"guests"."avatar_path",
"guests"."color",
"guests"."side",
"guests"."audio_recording_path",
"guests"."audio_recording_public",
"guests"."memo",
"guests"."memo_public",
"guests"."setup_at",
"guests"."last_upload",
"guests"."inserted_at",
"guests"."updated_at",
"userAssets"."id",
"userAssets"."kind",
"userAssets"."event_id",
"userAssets"."uploader_id",
"userAssets"."uploader_ip",
"userAssets"."path",
"userAssets"."file_size",
"userAssets"."width",
"userAssets"."height",
"userAssets"."visible_at",
"userAssets"."deleted_at",
"userAssets"."inserted_at",
"userAssets"."updated_at"
FROM
(
SELECT
"id",
"session_id",
"username",
"avatar_path",
"color",
"side",
"audio_recording_path",
"audio_recording_public",
"memo",
"memo_public",
"setup_at",
"last_upload",
"inserted_at",
"updated_at"
FROM
"guests"
ORDER BY
"guests"."last_upload" DESC,
"guests"."id" DESC
LIMIT
100
) "guests"
CROSS JOIN LATERAL (
SELECT
"id",
"kind",
"event_id",
"uploader_id",
"uploader_ip",
"path",
"file_size",
"width",
"height",
"visible_at",
"deleted_at",
"inserted_at",
"updated_at"
FROM
"assets"
WHERE
(
"assets"."event_id" = (
SELECT
"id"
FROM
"events"
WHERE
"events"."event_key" = '01JKCVP4M2CH34SVTQGHSW4Y5G'
)
AND "assets"."uploader_id" = "guests"."id"
)
ORDER BY
"assets"."inserted_at" DESC
LIMIT
100
) "userAssets";
View Explain Plan (before optimization)
{
"Node Type": "Nested Loop",
"Parallel Aware": false,
"Async Capable": false,
"Join Type": "Inner",
"Startup Cost": 159.35,
"Total Cost": 15922.03,
"Plan Rows": 100,
"Plan Width": 370,
"Inner Unique": false,
"Plans": [
{
"Node Type": "Limit",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Startup Cost": 0.16,
"Total Cost": 1.78,
"Plan Rows": 100,
"Plan Width": 238,
"Plans": [
{
"Node Type": "Index Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Scan Direction": "Forward",
"Index Name": "guests_last_upload_desc_id_desc_index",
"Relation Name": "guests",
"Alias": "guests",
"Startup Cost": 0.16,
"Total Cost": 162.16,
"Plan Rows": 10000,
"Plan Width": 238
}
]
},
{
"Node Type": "Limit",
"Parent Relationship": "Inner",
"Parallel Aware": false,
"Async Capable": false,
"Startup Cost": 159.19,
"Total Cost": 159.19,
"Plan Rows": 1,
"Plan Width": 132,
"Plans": [
{
"Node Type": "Index Scan",
"Parent Relationship": "InitPlan",
"Subplan Name": "InitPlan 1 (returns $0)",
"Parallel Aware": false,
"Async Capable": false,
"Scan Direction": "Forward",
"Index Name": "events_event_key_index",
"Relation Name": "events",
"Alias": "events",
"Startup Cost": 0.16,
"Total Cost": 8.18,
"Plan Rows": 1,
"Plan Width": 8,
"Index Cond": "(event_key = '01JKCVP4M2CH34SVTQGHSW4Y5G'::text)"
},
{
"Node Type": "Sort",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Startup Cost": 151.01,
"Total Cost": 151.01,
"Plan Rows": 1,
"Plan Width": 132,
"Sort Key": [
"assets.inserted_at DESC"
],
"Plans": [
{
"Node Type": "Seq Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Relation Name": "assets",
"Alias": "assets",
"Startup Cost": 0,
"Total Cost": 151,
"Plan Rows": 1,
"Plan Width": 132,
"Filter": "((event_id = $0) AND (uploader_id = guests.id))"
}
]
}
]
}
]
}View Explain Plan (after optimization)
{
"Node Type": "Nested Loop",
"Parallel Aware": false,
"Async Capable": false,
"Join Type": "Inner",
"Startup Cost": 8.5,
"Total Cost": 1638.53,
"Plan Rows": 100,
"Plan Width": 370,
"Inner Unique": false,
"Plans": [
{
"Node Type": "Limit",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Startup Cost": 0.16,
"Total Cost": 1.78,
"Plan Rows": 100,
"Plan Width": 238,
"Plans": [
{
"Node Type": "Index Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Scan Direction": "Forward",
"Index Name": "guests_last_upload_desc_id_desc_index",
"Relation Name": "guests",
"Alias": "guests",
"Startup Cost": 0.16,
"Total Cost": 162.16,
"Plan Rows": 10000,
"Plan Width": 238
}
]
},
{
"Node Type": "Limit",
"Parent Relationship": "Inner",
"Parallel Aware": false,
"Async Capable": false,
"Startup Cost": 8.34,
"Total Cost": 16.36,
"Plan Rows": 1,
"Plan Width": 132,
"Plans": [
{
"Node Type": "Index Scan",
"Parent Relationship": "InitPlan",
"Subplan Name": "InitPlan 1 (returns $0)",
"Parallel Aware": false,
"Async Capable": false,
"Scan Direction": "Forward",
"Index Name": "events_event_key_index",
"Relation Name": "events",
"Alias": "events",
"Startup Cost": 0.16,
"Total Cost": 8.18,
"Plan Rows": 1,
"Plan Width": 8,
"Index Cond": "(event_key = '01JKCVP4M2CH34SVTQGHSW4Y5G'::text)"
},
{
"Node Type": "Index Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Scan Direction": "Forward",
"Index Name": "assets(event_id, uploader_id, inserted_at desc)",
"Relation Name": "assets",
"Alias": "assets",
"Startup Cost": 0.16,
"Total Cost": 8.18,
"Plan Rows": 1,
"Plan Width": 132,
"Index Cond": "((event_id = $0) AND (uploader_id = guests.id))"
}
]
}
]
}Query 62cddf9e2b2dad50
New indexes improve cost by 13.94x:guest_ip_addresses(ip_address)
SELECT
*
FROM
guest_ip_addresses
WHERE
ip_address = '127.0.0.1';
View Explain Plan (before optimization)
{
"Node Type": "Seq Scan",
"Parallel Aware": false,
"Async Capable": false,
"Relation Name": "guest_ip_addresses",
"Alias": "guest_ip_addresses",
"Startup Cost": 0,
"Total Cost": 126,
"Plan Rows": 50,
"Plan Width": 56,
"Filter": "(ip_address = '127.0.0.1'::text)"
}View Explain Plan (after optimization)
{
"Node Type": "Index Scan",
"Parallel Aware": false,
"Async Capable": false,
"Scan Direction": "Forward",
"Index Name": "guest_ip_addresses(ip_address)",
"Relation Name": "guest_ip_addresses",
"Alias": "guest_ip_addresses",
"Startup Cost": 0.16,
"Total Cost": 9.04,
"Plan Rows": 50,
"Plan Width": 56,
"Index Cond": "(ip_address = '127.0.0.1'::text)"
}What are the numbers next to the query?
The numbers are a fingerprint uniquely identifying the query. Let us know in the Discord if you'd like to be able to assign unique names to your queries.What is cost?
Cost is an arbitrary value representing the amount of work postgres decided it needs to do to execute a query based on what it knows about the database and the query itself.We use cost to look for improvements when checking if an index helps optimize a query in CI as the full production dataset is simply not available to work with.
Execution metadata
- Log size
- 209261 bytes
- Time elapsed
- 2201ms
- Queries Seen
- 26
- Queries matched
- 10
- Queries optimized
- 2
- Queries errored
- 1
https://github.com/Query-Doctor/analyzer/blob/1b5e873/src/sync/syncer.ts#L96-L97 it seems like it's still in use? |
Summary
json.ts,errors.ts,schema_differ.ts,sanitize.ts,recent-query.ts,config.ts,dependency-tree.tsDetails
json.ts\r/\tpreservation, whitespace-only input, non-whitespace prefixerrors.tsinstanceofschema_differ.tsConnectable.fromString)sanitize.tsrecent-query.tswithOptimization,analyzeintegration, static helpersconfig.tslastSeenQueriesdependency-tree.tsmaxRows/incomplete_dependency_chain, error paths,onStartAnalyzeTest plan
npx vitest run)🤖 Generated with Claude Code