Skip to content

Add modular response compression middleware (Zstd + Gzip) - #85

Open
beauremus with Copilot wants to merge 123 commits into
mainfrom
copilot/add-compression-layer-middleware
Open

Add modular response compression middleware (Zstd + Gzip)#85
beauremus with Copilot wants to merge 123 commits into
mainfrom
copilot/add-compression-layer-middleware

Conversation

Copilot AI commented Mar 30, 2026

Copy link
Copy Markdown
  • Explore repo structure and understand router setup
  • Add compression-zstd and compression-gzip features to tower-http in Cargo.toml
  • Create src/graphql/compression.rs module with a compression_layer() function returning a CompressionLayer (Zstd + Gzip)
  • Register mod compression and apply the layer in create_site() in src/graphql/mod.rs
  • Fix compression test: replace naive content-encoding assertion with match-based check — gzip/zstd expect the header, unsupported encodings (e.g. deflate) expect no header

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a dedicated GraphQL compression module and wires it into the Axum router to enable response compression (preferring Zstd with Gzip fallback) via tower_http::compression::CompressionLayer.

Changes:

  • Enabled tower-http gzip + zstd compression features in Cargo.toml (and updated Cargo.lock accordingly).
  • Added src/graphql/compression.rs exposing a single compression_layer() helper.
  • Applied the compression layer to the main GraphQL router and added a compression-focused test.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.

File Description
Cargo.toml Enables tower-http compression feature flags for gzip/zstd.
Cargo.lock Captures new transitive deps (notably zstd-related crates).
src/graphql/compression.rs Introduces isolated compression_layer() construction.
src/graphql/mod.rs Registers module, applies the layer to routers, and adds a compression test.

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

Comment thread src/graphql/mod.rs
Comment on lines +483 to +486
check_compression("gzip").await;
check_compression("zstd").await;
check_compression("deflate").await;
}

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

test_compression asserts behavior for Accept-Encoding: deflate, but compression_layer() is only configured to enable Zstd and Gzip. If deflate isn't enabled by default, this test will fail (and it also contradicts the PR description). Consider either enabling deflate explicitly in compression_layer() or adjusting this assertion to expect no content-encoding (or to only test gzip/zstd).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in commit 46775e8. The check_compression closure now uses a match on the encoding name: "gzip" and "zstd" assert the content-encoding header is present and matches; any other encoding (including "deflate") asserts no content-encoding header is set, correctly reflecting that only Gzip and Zstd are enabled in compression_layer().

Comment thread src/graphql/mod.rs Outdated
@github-actions

Copy link
Copy Markdown

Code Coverage Report - 1,671 of 3,971 lines covered ( ⛔ 42.08%)

src - 1,671 of 3,971 lines covered ( ⛔ 42.08%)

src/g_rpc - 135 of 1,185 lines covered ( ⛔ 11.39%)

src/g_rpc/alarms_db - 37 of 51 lines covered ( ⛔ 72.55%)

src/g_rpc/alarms_db/groups - 12 of 16 lines covered ( ⛔ 75.00%)

src/g_rpc/alarms_db/groups/mod.rs - 12 of 16 lines covered ( ⛔ 75.00%)

Uncovered lines: ⚠️ 16-17, 26-27

src/g_rpc/alarms_db/layouts - 4 of 6 lines covered ( ⛔ 66.67%)

src/g_rpc/alarms_db/layouts/mod.rs - 4 of 6 lines covered ( ⛔ 66.67%)

Uncovered lines: ⚠️ 13-14

src/g_rpc/alarms_db/mod.rs - 3 of 3 lines covered ( 💎 100.00%)

:shipit:

src/g_rpc/alarms_db/timers - 18 of 26 lines covered ( ⛔ 69.23%)

src/g_rpc/alarms_db/timers/mod.rs - 18 of 26 lines covered ( ⛔ 69.23%)

Uncovered lines: ⚠️ 16-17, 24-25, 34-35, 42-43

src/g_rpc/alarms_svc - 35 of 43 lines covered ( ⚠️ 81.40%)

src/g_rpc/alarms_svc/mod.rs - 35 of 43 lines covered ( ⚠️ 81.40%)

Uncovered lines: ⚠️ 38-39, 52-53, 70-71, 82, 85

src/g_rpc/clock - 0 of 19 lines covered ( ⛔ 0.00%)

src/g_rpc/clock/mod.rs - 0 of 19 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/connection_utils - 35 of 50 lines covered ( ⛔ 70.00%)

src/g_rpc/connection_utils/mod.rs - 35 of 50 lines covered ( ⛔ 70.00%)

Uncovered lines: ⚠️ 49-53, 58-61, 78, 82, 85, 88-90

src/g_rpc/devdb - 0 of 12 lines covered ( ⛔ 0.00%)

src/g_rpc/devdb/mod.rs - 0 of 12 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/dpm - 0 of 32 lines covered ( ⛔ 0.00%)

src/g_rpc/dpm/mod.rs - 0 of 32 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated - 28 of 860 lines covered ( ⛔ 3.26%)

src/g_rpc/generated/common.device.rs - 0 of 85 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/common.status.rs - 0 of 11 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_commands.v1.rs - 0 of 113 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_groups.rs - 7 of 95 lines covered ( ⛔ 7.37%)

Uncovered lines: ⚠️ 62, 72-95, 97-98, 104-107, 110-113, 118-121, 126-142, 144-146, 149-154, 157-168, 170-172, 175-180, 183-184

src/g_rpc/generated/services.alarm_timers.rs - 17 of 139 lines covered ( ⛔ 12.23%)

Uncovered lines: ⚠️ 84-87, 96-119, 121-122, 128-131, 134-137, 142-145, 150-163, 165-167, 170-173, 175-186, 188-190, 193-196, 198-209, 211-213, 216-219, 221-232, 234-236, 239-242, 244-245

src/g_rpc/generated/services.alarm_user_layouts.rs - 4 of 71 lines covered ( ⛔ 5.63%)

Uncovered lines: ⚠️ 35-38, 47-70, 72-73, 79-82, 85-88, 93-96, 101-114, 116-118, 121-126, 129-130

src/g_rpc/generated/services.clock_event.rs - 0 of 71 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.daq.rs - 0 of 79 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.tlg_placement.rs - 0 of 173 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/third_party.r#type.rs - 0 of 23 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/mod.rs - 0 of 42 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/tlg - 0 of 39 lines covered ( ⛔ 0.00%)

src/g_rpc/tlg/mod.rs - 0 of 39 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/wscan - 0 of 37 lines covered ( ⛔ 0.00%)

src/g_rpc/wscan/mod.rs - 0 of 37 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql - 1,426 of 2,625 lines covered ( ⛔ 54.32%)

src/graphql/acsys - 785 of 1,337 lines covered ( ⛔ 58.71%)

src/graphql/acsys/datastream - 608 of 687 lines covered ( ✅ 88.50%)

src/graphql/acsys/datastream/archivestream.rs - 0 of 20 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/acsys/datastream/datachannel.rs - 62 of 70 lines covered ( ✅ 88.57%)

Uncovered lines: ⚠️ 32-34, 36, 38-39, 82-83

src/graphql/acsys/datastream/datamerge.rs - 143 of 162 lines covered ( ✅ 88.27%)

Uncovered lines: ⚠️ 98, 103, 106-107, 121, 152, 161, 166, 169-170, 180-181, 184, 186, 194-197, 202

src/graphql/acsys/datastream/endondate.rs - 155 of 156 lines covered ( ✅ 99.36%)

Uncovered lines: ⚠️ 72

src/graphql/acsys/datastream/filterdupes.rs - 92 of 92 lines covered ( 💎 100.00%)

:shipit:

src/graphql/acsys/datastream/groupscalars.rs - 156 of 171 lines covered ( ✅ 91.23%)

Uncovered lines: ⚠️ 65, 73, 89-92, 94-97, 119, 122, 180-181, 185

src/graphql/acsys/datastream/mod.rs - 0 of 16 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/acsys/mod.rs - 134 of 554 lines covered ( ⛔ 24.19%)

Uncovered lines: ⚠️ 26-28, 35-41, 46-67, 69-79, 81, 83-96, 99, 117-124, 129, 172, 233-243, 278, 314-317, 335, 407, 410-418, 420-421, 423-426, 428-432, 434-436, 439-442, 444, 447-451, 459-462, 467-468, 472-478, 480, 482-485, 487-489, 491, 493-494, 496, 503-505, 511, 513-515, 517-520, 522, 524, 530-532, 535-536, 538-543, 546, 548-550, 552-560, 562-563, 565, 567-575, 578, 580, 582, 585-588, 603, 608, 611-613, 617-620, 622, 624, 627, 629, 633, 638-640, 680, 684-699, 701-703, 706-716, 723, 726-736, 741-758, 761-762, 764, 766, 768-769, 771, 773, 817-820, 827-836, 838-840, 851-852, 854, 856-865, 868, 923, 956-957, 970-1003, 1013-1015, 1030-1032, 1034, 1036-1037, 1085-1088, 1098, 1108-1155, 1161, 1165, 1181

src/graphql/acsys/plotconfigdb.rs - 43 of 94 lines covered ( ⛔ 45.74%)

Uncovered lines: ⚠️ 24-26, 29, 33, 35, 37, 39-43, 45-47, 59-62, 68-69, 71, 77-79, 114-116, 118-122, 124-128, 130-134, 136-140, 142-144

src/graphql/acsys/types.rs - 0 of 2 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/alarms - 495 of 530 lines covered ( ✅ 93.40%)

src/graphql/alarms/mod.rs - 306 of 341 lines covered ( ✅ 89.74%)

Uncovered lines: ⚠️ 35, 43, 62, 77, 87, 107, 123-129, 144-150, 159-165, 174, 189-195

src/graphql/alarms/types.rs - 122 of 122 lines covered ( 💎 100.00%)

:shipit:

src/graphql/alarms/utils.rs - 67 of 67 lines covered ( 💎 100.00%)

:shipit:

src/graphql/bbm - 0 of 116 lines covered ( ⛔ 0.00%)

src/graphql/bbm/mod.rs - 0 of 31 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/bbm/types.rs - 0 of 85 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/compression.rs - 3 of 3 lines covered ( 💎 100.00%)

:shipit:

src/graphql/devdb - 0 of 104 lines covered ( ⛔ 0.00%)

src/graphql/devdb/mod.rs - 0 of 97 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/devdb/types.rs - 0 of 7 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/faas - 0 of 80 lines covered ( ⛔ 0.00%)

src/graphql/faas/mod.rs - 0 of 80 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/mod.rs - 132 of 252 lines covered ( ⛔ 52.38%)

Uncovered lines: ⚠️ 55-75, 113, 117-120, 122-135, 137-138, 143, 146-148, 150-154, 156-160, 162, 167, 170-173, 175-179, 181-185, 187, 189, 192-194, 196-200, 202-204, 206, 208, 211-213, 215-219, 221-223, 225, 230, 234-237, 239, 241-246, 248-253, 255-256, 289

src/graphql/scanner - 0 of 41 lines covered ( ⛔ 0.00%)

src/graphql/scanner/mod.rs - 0 of 16 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/scanner/types.rs - 0 of 25 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/tlg - 0 of 48 lines covered ( ⛔ 0.00%)

src/graphql/tlg/mod.rs - 0 of 25 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/tlg/types.rs - 0 of 23 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/types.rs - 11 of 114 lines covered ( ⛔ 9.65%)

Uncovered lines: ⚠️ 32-34, 36-43, 45, 47, 49-50, 184, 201-202, 205, 211-213, 216, 221-223, 227, 231-235, 240, 243-245, 251, 253-255, 262-267, 275-277, 279, 288-291, 293-296, 298-302, 304-307, 309-316, 318, 324-327, 329-332, 334-343, 345-352, 354

src/main.rs - 0 of 13 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/pubsub - 110 of 148 lines covered ( ⛔ 74.32%)

src/pubsub/mod.rs - 110 of 148 lines covered ( ⛔ 74.32%)

Uncovered lines: ⚠️ 81-84, 94, 96, 125, 127, 129-132, 134-137, 140-142, 146, 182-191, 193, 195-196, 198, 200, 224-226

@github-actions

Copy link
Copy Markdown

Code Coverage Report - 1,668 of 3,968 lines covered ( ⛔ 42.04%)

src - 1,668 of 3,968 lines covered ( ⛔ 42.04%)

src/g_rpc - 135 of 1,185 lines covered ( ⛔ 11.39%)

src/g_rpc/alarms_db - 37 of 51 lines covered ( ⛔ 72.55%)

src/g_rpc/alarms_db/groups - 12 of 16 lines covered ( ⛔ 75.00%)

src/g_rpc/alarms_db/groups/mod.rs - 12 of 16 lines covered ( ⛔ 75.00%)

Uncovered lines: ⚠️ 16-17, 26-27

src/g_rpc/alarms_db/layouts - 4 of 6 lines covered ( ⛔ 66.67%)

src/g_rpc/alarms_db/layouts/mod.rs - 4 of 6 lines covered ( ⛔ 66.67%)

Uncovered lines: ⚠️ 13-14

src/g_rpc/alarms_db/mod.rs - 3 of 3 lines covered ( 💎 100.00%)

:shipit:

src/g_rpc/alarms_db/timers - 18 of 26 lines covered ( ⛔ 69.23%)

src/g_rpc/alarms_db/timers/mod.rs - 18 of 26 lines covered ( ⛔ 69.23%)

Uncovered lines: ⚠️ 16-17, 24-25, 34-35, 42-43

src/g_rpc/alarms_svc - 35 of 43 lines covered ( ⚠️ 81.40%)

src/g_rpc/alarms_svc/mod.rs - 35 of 43 lines covered ( ⚠️ 81.40%)

Uncovered lines: ⚠️ 38-39, 52-53, 70-71, 82, 85

src/g_rpc/clock - 0 of 19 lines covered ( ⛔ 0.00%)

src/g_rpc/clock/mod.rs - 0 of 19 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/connection_utils - 35 of 50 lines covered ( ⛔ 70.00%)

src/g_rpc/connection_utils/mod.rs - 35 of 50 lines covered ( ⛔ 70.00%)

Uncovered lines: ⚠️ 49-53, 58-61, 78, 82, 85, 88-90

src/g_rpc/devdb - 0 of 12 lines covered ( ⛔ 0.00%)

src/g_rpc/devdb/mod.rs - 0 of 12 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/dpm - 0 of 32 lines covered ( ⛔ 0.00%)

src/g_rpc/dpm/mod.rs - 0 of 32 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated - 28 of 860 lines covered ( ⛔ 3.26%)

src/g_rpc/generated/common.device.rs - 0 of 85 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/common.status.rs - 0 of 11 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_commands.v1.rs - 0 of 113 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_groups.rs - 7 of 95 lines covered ( ⛔ 7.37%)

Uncovered lines: ⚠️ 62, 72-95, 97-98, 104-107, 110-113, 118-121, 126-142, 144-146, 149-154, 157-168, 170-172, 175-180, 183-184

src/g_rpc/generated/services.alarm_timers.rs - 17 of 139 lines covered ( ⛔ 12.23%)

Uncovered lines: ⚠️ 84-87, 96-119, 121-122, 128-131, 134-137, 142-145, 150-163, 165-167, 170-173, 175-186, 188-190, 193-196, 198-209, 211-213, 216-219, 221-232, 234-236, 239-242, 244-245

src/g_rpc/generated/services.alarm_user_layouts.rs - 4 of 71 lines covered ( ⛔ 5.63%)

Uncovered lines: ⚠️ 35-38, 47-70, 72-73, 79-82, 85-88, 93-96, 101-114, 116-118, 121-126, 129-130

src/g_rpc/generated/services.clock_event.rs - 0 of 71 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.daq.rs - 0 of 79 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.tlg_placement.rs - 0 of 173 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/third_party.r#type.rs - 0 of 23 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/mod.rs - 0 of 42 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/tlg - 0 of 39 lines covered ( ⛔ 0.00%)

src/g_rpc/tlg/mod.rs - 0 of 39 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/wscan - 0 of 37 lines covered ( ⛔ 0.00%)

src/g_rpc/wscan/mod.rs - 0 of 37 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql - 1,423 of 2,622 lines covered ( ⛔ 54.27%)

src/graphql/acsys - 785 of 1,337 lines covered ( ⛔ 58.71%)

src/graphql/acsys/datastream - 608 of 687 lines covered ( ✅ 88.50%)

src/graphql/acsys/datastream/archivestream.rs - 0 of 20 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/acsys/datastream/datachannel.rs - 62 of 70 lines covered ( ✅ 88.57%)

Uncovered lines: ⚠️ 32-34, 36, 38-39, 82-83

src/graphql/acsys/datastream/datamerge.rs - 143 of 162 lines covered ( ✅ 88.27%)

Uncovered lines: ⚠️ 98, 103, 106-107, 121, 152, 161, 166, 169-170, 180-181, 184, 186, 194-197, 202

src/graphql/acsys/datastream/endondate.rs - 155 of 156 lines covered ( ✅ 99.36%)

Uncovered lines: ⚠️ 72

src/graphql/acsys/datastream/filterdupes.rs - 92 of 92 lines covered ( 💎 100.00%)

:shipit:

src/graphql/acsys/datastream/groupscalars.rs - 156 of 171 lines covered ( ✅ 91.23%)

Uncovered lines: ⚠️ 65, 73, 89-92, 94-97, 119, 122, 180-181, 185

src/graphql/acsys/datastream/mod.rs - 0 of 16 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/acsys/mod.rs - 134 of 554 lines covered ( ⛔ 24.19%)

Uncovered lines: ⚠️ 26-28, 35-41, 46-67, 69-79, 81, 83-96, 99, 117-124, 129, 172, 233-243, 278, 314-317, 335, 407, 410-418, 420-421, 423-426, 428-432, 434-436, 439-442, 444, 447-451, 459-462, 467-468, 472-478, 480, 482-485, 487-489, 491, 493-494, 496, 503-505, 511, 513-515, 517-520, 522, 524, 530-532, 535-536, 538-543, 546, 548-550, 552-560, 562-563, 565, 567-575, 578, 580, 582, 585-588, 603, 608, 611-613, 617-620, 622, 624, 627, 629, 633, 638-640, 680, 684-699, 701-703, 706-716, 723, 726-736, 741-758, 761-762, 764, 766, 768-769, 771, 773, 817-820, 827-836, 838-840, 851-852, 854, 856-865, 868, 923, 956-957, 970-1003, 1013-1015, 1030-1032, 1034, 1036-1037, 1085-1088, 1098, 1108-1155, 1161, 1165, 1181

src/graphql/acsys/plotconfigdb.rs - 43 of 94 lines covered ( ⛔ 45.74%)

Uncovered lines: ⚠️ 24-26, 29, 33, 35, 37, 39-43, 45-47, 59-62, 68-69, 71, 77-79, 114-116, 118-122, 124-128, 130-134, 136-140, 142-144

src/graphql/acsys/types.rs - 0 of 2 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/alarms - 495 of 530 lines covered ( ✅ 93.40%)

src/graphql/alarms/mod.rs - 306 of 341 lines covered ( ✅ 89.74%)

Uncovered lines: ⚠️ 35, 43, 62, 77, 87, 107, 123-129, 144-150, 159-165, 174, 189-195

src/graphql/alarms/types.rs - 122 of 122 lines covered ( 💎 100.00%)

:shipit:

src/graphql/alarms/utils.rs - 67 of 67 lines covered ( 💎 100.00%)

:shipit:

src/graphql/bbm - 0 of 116 lines covered ( ⛔ 0.00%)

src/graphql/bbm/mod.rs - 0 of 31 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/bbm/types.rs - 0 of 85 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/compression.rs - 3 of 3 lines covered ( 💎 100.00%)

:shipit:

src/graphql/devdb - 0 of 104 lines covered ( ⛔ 0.00%)

src/graphql/devdb/mod.rs - 0 of 97 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/devdb/types.rs - 0 of 7 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/faas - 0 of 80 lines covered ( ⛔ 0.00%)

src/graphql/faas/mod.rs - 0 of 80 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/mod.rs - 129 of 249 lines covered ( ⛔ 51.81%)

Uncovered lines: ⚠️ 55-75, 113, 117-120, 122-135, 137-138, 143, 146-148, 150-154, 156-160, 162, 167, 170-173, 175-179, 181-185, 187, 189, 192-194, 196-200, 202-204, 206, 208, 211-213, 215-219, 221-223, 225, 230, 234-237, 239, 241-246, 248-253, 255-256, 289

src/graphql/scanner - 0 of 41 lines covered ( ⛔ 0.00%)

src/graphql/scanner/mod.rs - 0 of 16 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/scanner/types.rs - 0 of 25 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/tlg - 0 of 48 lines covered ( ⛔ 0.00%)

src/graphql/tlg/mod.rs - 0 of 25 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/tlg/types.rs - 0 of 23 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/types.rs - 11 of 114 lines covered ( ⛔ 9.65%)

Uncovered lines: ⚠️ 32-34, 36-43, 45, 47, 49-50, 184, 201-202, 205, 211-213, 216, 221-223, 227, 231-235, 240, 243-245, 251, 253-255, 262-267, 275-277, 279, 288-291, 293-296, 298-302, 304-307, 309-316, 318, 324-327, 329-332, 334-343, 345-352, 354

src/main.rs - 0 of 13 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/pubsub - 110 of 148 lines covered ( ⛔ 74.32%)

src/pubsub/mod.rs - 110 of 148 lines covered ( ⛔ 74.32%)

Uncovered lines: ⚠️ 81-84, 94, 96, 125, 127, 129-132, 134-137, 140-142, 146, 182-191, 193, 195-196, 198, 200, 224-226

Parse the content-encoding header and assert only the encodings this middleware guarantees (gzip/zstd), with a clear expectation for unsupported encodings.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI requested a review from beauremus March 30, 2026 21:03

@rneswold rneswold left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good! Loving the AI assistants!

@jacob-curley-fnal jacob-curley-fnal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Had some questions about the strategies used in one of the test cases, but not a showstopper! And the questions are mostly for my curiosity, I haven't checked whether the suggestion would actually compile.

Comment thread src/graphql/mod.rs
Comment on lines +444 to +494
async fn test_compression() {
let site = mk_test_site();
let query = r#"{ "query" : "{ __schema { types { name } } }" }"#;

// Helper to check compression
let check_compression = |encoding: &str| {
let mut site = site.clone();
let query = query.to_string();
let encoding = encoding.to_string();
async move {
let response = site
.as_service()
.call(
Request::builder()
.method("POST")
.uri("/test")
.header("content-type", "application/json")
.header("accept-encoding", &encoding)
.body(Body::from(query))
.unwrap(),
)
.await
.unwrap();

assert_eq!(response.status(), StatusCode::OK);
let header = response.headers().get("content-encoding");
match encoding.as_str() {
"gzip" | "zstd" => {
let value = header
.expect("missing content-encoding header for supported encoding")
.to_str()
.expect("invalid content-encoding header value");
assert_eq!(value, encoding);
}
_ => {
// For unsupported encodings (e.g., deflate), we expect no compression.
assert!(
header.is_none(),
"expected no content-encoding header for unsupported encoding {}, got {:?}",
encoding,
header
);
}
}
}
};

check_compression("gzip").await;
check_compression("zstd").await;
check_compression("deflate").await;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit - questions about this test case:
1.) Why initialize query as a byte string, if we're just gonna call to_string on it later (and then call as_str on it after that)? Could it just be initialized as a &str out of the gate?

let query = "{ \"query\" : \"{ __schema { types { name } } }\" }";

2.) Is Rust complaining about references to these local variables within the lambda? Is that why there's the async move block in there? Or would a reference work just fine? We only use the lambda within this same test function, so those variables should stay in scope, no? Would something like this work?:

Suggested change
async fn test_compression() {
let site = mk_test_site();
let query = r#"{ "query" : "{ __schema { types { name } } }" }"#;
// Helper to check compression
let check_compression = |encoding: &str| {
let mut site = site.clone();
let query = query.to_string();
let encoding = encoding.to_string();
async move {
let response = site
.as_service()
.call(
Request::builder()
.method("POST")
.uri("/test")
.header("content-type", "application/json")
.header("accept-encoding", &encoding)
.body(Body::from(query))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let header = response.headers().get("content-encoding");
match encoding.as_str() {
"gzip" | "zstd" => {
let value = header
.expect("missing content-encoding header for supported encoding")
.to_str()
.expect("invalid content-encoding header value");
assert_eq!(value, encoding);
}
_ => {
// For unsupported encodings (e.g., deflate), we expect no compression.
assert!(
header.is_none(),
"expected no content-encoding header for unsupported encoding {}, got {:?}",
encoding,
header
);
}
}
}
};
check_compression("gzip").await;
check_compression("zstd").await;
check_compression("deflate").await;
}
async fn test_compression() {
let site = mk_test_site();
let query = "{ \"query\" : \"{ __schema { types { name } } }\" }"; // could get by with just a &str
// Helper to check compression
let check_compression = |encoding: &str| async {
let response = site.clone()
.as_service()
.call(
Request::builder()
.method("POST")
.uri("/test")
.header("content-type", "application/json")
.header("accept-encoding", encoding) // encoding is already a reference
.body(Body::from(query.to_string())) // If the `to_string` is needed, but sometimes it's not - check whether `Body::from` will accept an &str
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let header = response.headers().get("content-encoding");
match encoding {
"gzip" | "zstd" => {
let value = header
.expect("missing content-encoding header for supported encoding")
.to_str()
.expect("invalid content-encoding header value");
assert_eq!(value, encoding);
}
_ => {
// For unsupported encodings (e.g., deflate), we expect no compression.
assert!(
header.is_none(),
"expected no content-encoding header for unsupported encoding {}, got {:?}",
encoding,
header
);
}
}
};
check_compression("gzip").await;
check_compression("zstd").await;
check_compression("deflate").await;
}

@rneswold rneswold Apr 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

r#"string"# isn't a byte string, it's a raw string (i.e. the compiler won't interpret escape characters in it.) But you're right, no need to make a string and then borrow a string slice.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does the change compile?

I know you sometimes have to move data into closures when dealing with Futures because the .await may cause the future to make further progress on another thread's stack.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, that's a good question. I was wondering if the borrow checker would be smart enough to see that we're only passing references in this case, and they refer to data that will outlive the lambda. But it's not always able to spot that, so good to check before committing the change.

Looking at this again, the problem might stem from this test case going after 3 distinct things, which may be better to split out into their own test cases. Then the lambda could be promoted to a standalone function that gets called by each of the test cases. But just a suggestion!

rneswold added 24 commits July 13, 2026 13:55
This was causing a compile-time error when running `cargo test` with no
default features enabled.
We don't conditionally include `set_device` so we don't need to decorate
it with the underscore (which prevents "unused symbol" warnings.)
Remove chatty `info` and add a `warn` message when credentials aren't
provided during a setting.
Still debugging settings
The filtering of data was working, however I realized that it was using
.partition_point() to split the vector into old and new data. The
partition function is efficient but relies on the timestamps to be
ascending. If DPM inserts a status, it inserts its own timestamp which
may be out of order with the front-end's timestamps, resulting in it
dropping data points.

This commit makes the filtering function work correctly.

An additional change was to improve the signature of the filtering
function. Before, it passed a `&mut Vec<>` which made it easy to use
various `Vec<>` methods. As a result, we were making 4 passes over the
data!

Now it takes an `Iterator<>` and we (can only) make one pass over the
data.
🚚 use newer module format
@rneswold

rneswold commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

I merged this PR's changes into main.

These were due to a mistake when merging `main`.
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Code Coverage Report - 3,133 of 6,388 lines covered ( ⛔ 49.05%)

src - 3,133 of 6,388 lines covered ( ⛔ 49.05%)

src/g_rpc - 191 of 2,196 lines covered ( ⛔ 8.70%)

src/g_rpc/alarms_db - 98 of 121 lines covered ( ⚠️ 80.99%)

src/g_rpc/alarms_db/groups.rs - 9 of 19 lines covered ( ⛔ 47.37%)

Uncovered lines: ⚠️ 24-28, 32-36

src/g_rpc/alarms_db/layouts.rs - 5 of 10 lines covered ( ⛔ 50.00%)

Uncovered lines: ⚠️ 20-24

src/g_rpc/alarms_db/timers.rs - 84 of 92 lines covered ( ✅ 91.30%)

Uncovered lines: ⚠️ 40-41, 61-62, 75-76, 103-104

src/g_rpc/alarms_db.rs - 8 of 13 lines covered ( ⛔ 61.54%)

Uncovered lines: ⚠️ 44-48

src/g_rpc/alarms_svc.rs - 50 of 68 lines covered ( ⛔ 73.53%)

Uncovered lines: ⚠️ 42-43, 56-57, 70-71, 80, 96-97, 101-105, 113-116

src/g_rpc/clock.rs - 0 of 19 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/connection_utils.rs - 35 of 50 lines covered ( ⛔ 70.00%)

Uncovered lines: ⚠️ 48-52, 57-60, 77, 81, 84, 87-89

src/g_rpc/devdb.rs - 0 of 71 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/dpm.rs - 0 of 34 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated - 0 of 1,744 lines covered ( ⛔ 0.00%)

src/g_rpc/generated/common.alarm.rs - 0 of 170 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/common.device.rs - 0 of 85 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/common.status.rs - 0 of 11 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_commands.rs - 0 of 254 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_groups.rs - 0 of 187 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_timers.rs - 0 of 248 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.alarm_user_layouts.rs - 0 of 133 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.clock_event.rs - 0 of 131 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.daq.rs - 0 of 189 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/services.tlg_placement.rs - 0 of 313 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/generated/third_party.r#type.rs - 0 of 23 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/tlg.rs - 0 of 39 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc/wscan.rs - 0 of 37 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/g_rpc.rs - 0 of 58 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql - 2,809 of 3,861 lines covered ( ⛔ 72.75%)

src/graphql/acsys - 1,821 of 1,916 lines covered ( ✅ 95.04%)

src/graphql/acsys/datastream - 1,821 of 1,843 lines covered ( ✅ 98.81%)

src/graphql/acsys/datastream/archivestream.rs - 289 of 289 lines covered ( 💎 100.00%)

:shipit:

src/graphql/acsys/datastream/datachannel.rs - 224 of 225 lines covered ( ✅ 99.56%)

Uncovered lines: ⚠️ 217

src/graphql/acsys/datastream/datamerge.rs - 545 of 564 lines covered ( ✅ 96.63%)

Uncovered lines: ⚠️ 145, 150, 167, 180-181, 186-187, 192, 204-206, 208-213, 218, 220

src/graphql/acsys/datastream/endondate.rs - 331 of 331 lines covered ( 💎 100.00%)

:shipit:

src/graphql/acsys/datastream/groupscalars.rs - 432 of 434 lines covered ( ✅ 99.54%)

Uncovered lines: ⚠️ 74, 189

src/graphql/acsys/datastream.rs - 0 of 14 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/acsys/types.rs - 0 of 59 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/acsys.rs - 433 of 898 lines covered ( ⛔ 48.22%)

Uncovered lines: ⚠️ 31-37, 42-58, 60-70, 72, 74-87, 90, 108-115, 120, 163, 172-174, 184-187, 204, 240-250, 272, 276-278, 283, 287-289, 305, 314-316, 327, 393-397, 406-410, 412-414, 416-417, 419-421, 423-424, 426-428, 430-431, 433-435, 437-441, 443-444, 447-448, 472, 475-479, 481-486, 488-489, 491-497, 499-502, 506-510, 518-521, 526-527, 531-537, 539, 541-544, 546-548, 550, 552-553, 555, 562-564, 570, 572-574, 576-579, 581, 583, 589-591, 594-595, 597-602, 605, 607-609, 611-619, 621-622, 624, 626-634, 637, 639, 641, 644-647, 662, 667, 670-672, 676-680, 682-683, 685, 687, 691, 693, 697, 702-705, 745, 749-764, 766-768, 771-781, 788, 791-801, 806-823, 826-827, 829, 831, 833-834, 836, 838, 886-889, 896-905, 907-909, 920-921, 923, 925-934, 937, 992, 1025-1026, 1039-1072, 1082-1084, 1099-1101, 1103, 1105-1106, 1153-1156, 1166, 1176-1224, 1230, 1234, 1250

src/graphql/alarms - 220 of 220 lines covered ( 💎 100.00%)

src/graphql/alarms/types - 133 of 133 lines covered ( 💎 100.00%)

src/graphql/alarms/types/tests.rs - 133 of 133 lines covered ( 💎 100.00%)

:shipit:

src/graphql/alarms/types.rs - 50 of 50 lines covered ( 💎 100.00%)

:shipit:

src/graphql/alarms/utils.rs - 37 of 37 lines covered ( 💎 100.00%)

:shipit:

src/graphql/alarms.rs - 325 of 360 lines covered ( ✅ 90.28%)

Uncovered lines: ⚠️ 36, 46, 56, 71, 81, 92, 108, 124-130, 141-147, 156-162, 181-187

src/graphql/bbm - 0 of 85 lines covered ( ⛔ 0.00%)

src/graphql/bbm/types.rs - 0 of 85 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/bbm.rs - 0 of 31 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/compression.rs - 3 of 3 lines covered ( 💎 100.00%)

:shipit:

src/graphql/devdb - 0 of 7 lines covered ( ⛔ 0.00%)

src/graphql/devdb/types.rs - 0 of 7 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/devdb.rs - 0 of 89 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/faas.rs - 0 of 80 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/scanner - 0 of 25 lines covered ( ⛔ 0.00%)

src/graphql/scanner/types.rs - 0 of 25 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/scanner.rs - 0 of 16 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/tlg - 0 of 25 lines covered ( ⛔ 0.00%)

src/graphql/tlg/types.rs - 0 of 25 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/tlg.rs - 0 of 25 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

src/graphql/types.rs - 7 of 81 lines covered ( ⛔ 8.64%)

Uncovered lines: ⚠️ 25-27, 29-33, 35-40, 174, 192-193, 196, 202-204, 207, 212-214, 218, 222-226, 231, 234-236, 242, 244-246, 253-258, 266-268, 270, 280-283, 285-288, 290-291, 293-294, 296-299, 301-308, 310

src/graphql.rs - 133 of 260 lines covered ( ⛔ 51.15%)

Uncovered lines: ⚠️ 63-83, 120, 127-132, 135-141, 143-148, 150, 173, 178, 181-183, 185-189, 191-195, 197, 202, 205-208, 210-214, 216-220, 222, 224, 227-229, 231-235, 237-239, 241, 243, 246-248, 250-254, 256-258, 260, 265, 269-272, 274, 276-281, 283-288, 290-291, 326, 357-359, 364-366

src/main.rs - 0 of 13 lines covered ( ⛔ 0.00%)

⛔ This file is missing coverage.

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.

5 participants