Created
March 3, 2026 13:53
-
-
Save musketyr/2ed7bb737712b42ce7636946b61d8734 to your computer and use it in GitHub Desktop.
Spock to JUnit 5 Migration: GoogleAnalyticsResponseUtilsSpec (sc185894)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Spock → JUnit 5 Migration: GoogleAnalyticsResponseUtilsSpec</title> | |
| <style> | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; padding: 20px; line-height: 1.6; } | |
| h1 { text-align: center; margin-bottom: 20px; color: #333; } | |
| .section { background: white; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); overflow: hidden; } | |
| .section-header { background: #2d3748; color: white; padding: 12px 16px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; } | |
| .section-header:hover { background: #4a5568; } | |
| .section-header h2 { font-size: 16px; font-weight: 600; } | |
| .section-header .toggle { font-size: 12px; } | |
| .section-content { display: block; } | |
| .section-content.collapsed { display: none; } | |
| .diff-container { display: grid; grid-template-columns: 1fr 1fr; } | |
| .diff-panel { padding: 16px; overflow-x: auto; } | |
| .diff-panel.left { background: #fff5f5; border-right: 1px solid #e2e8f0; } | |
| .diff-panel.right { background: #f0fff4; } | |
| .diff-panel h3 { font-size: 12px; text-transform: uppercase; color: #718096; margin-bottom: 12px; letter-spacing: 0.5px; } | |
| pre { font-family: 'SF Mono', Monaco, 'Courier New', monospace; font-size: 13px; white-space: pre-wrap; word-wrap: break-word; } | |
| .left pre { color: #c53030; } | |
| .right pre { color: #276749; } | |
| .summary { background: #c6f6d5; border-left: 4px solid #38a169; padding: 12px 16px; margin: 0; } | |
| .summary p { color: #276749; font-size: 14px; } | |
| .summary strong { color: #22543d; } | |
| @media (max-width: 768px) { .diff-container { grid-template-columns: 1fr; } .diff-panel.left { border-right: none; border-bottom: 1px solid #e2e8f0; } } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>🦀 Spock → JUnit 5 Migration: GoogleAnalyticsResponseUtilsSpec</h1> | |
| <p style="text-align: center; color: #666; margin-bottom: 20px;">Story: <a href="https://app.shortcut.com/agorapulse/story/185894">sc185894</a> | PR: <a href="https://github.com/agorapulse/platform/pull/73543">#73543</a></p> | |
| <!-- Section: Imports --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>📦 Imports</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>package agorapulse.roi.service.google.client.analytics.api | |
| import agorapulse.roi.service.google.client.analytics.api.Ga4InsightsApiResponse.Header | |
| import agorapulse.roi.service.google.client.analytics.api.Ga4InsightsApiResponse.Row | |
| import agorapulse.roi.service.google.client.analytics.api.Ga4InsightsApiResponse.Value | |
| import spock.lang.Specification</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>package agorapulse.roi.service.google.client.analytics.api; | |
| import agorapulse.roi.service.google.client.analytics.api.Ga4InsightsApiResponse.Header; | |
| import agorapulse.roi.service.google.client.analytics.api.Ga4InsightsApiResponse.Row; | |
| import agorapulse.roi.service.google.client.analytics.api.Ga4InsightsApiResponse.Value; | |
| import org.junit.jupiter.api.Test; | |
| import java.util.List; | |
| import java.util.Map; | |
| import static org.assertj.core.api.Assertions.assertThat; | |
| import static org.assertj.core.api.Assertions.assertThatThrownBy;</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Replaced Spock Specification import with JUnit 5 @Test and AssertJ assertions. Added Java utility imports (List, Map).</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section: Class & Fields --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>🏗️ Class & Fields</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>class GoogleAnalyticsResponseUtilsSpec extends Specification { | |
| }</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>class GoogleAnalyticsResponseUtilsTest { | |
| private Row createRow(List<String> dimensionValues, List<String> metricValues) { | |
| Row row = new Row(); | |
| row.setDimensionValues(dimensionValues.stream().map(Value::new).toList()); | |
| row.setMetricValues(metricValues.stream().map(Value::new).toList()); | |
| return row; | |
| } | |
| private Row createRowWithMetricsOnly(List<String> metricValues) { | |
| Row row = new Row(); | |
| row.setMetricValues(metricValues.stream().map(Value::new).toList()); | |
| return row; | |
| } | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Removed Specification inheritance. Added helper methods createRow() and createRowWithMetricsOnly() to replace Groovy's concise object construction syntax.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section: Test 1 --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>🧪 Should map response headers with values</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>void 'Should map response headers with values'() { | |
| given: | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date')], | |
| metricHeaders: [new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '2.0'), new Value(value: '7'), new Value(value: '17'), new Value(value: '29.0')]), | |
| ] | |
| ) | |
| when: | |
| Map<String, String> response = GoogleAnalyticsResponseUtils.mapHeadersWithValues(eventsResponse.metricHeaders, eventsResponse.rows.get(0).metricValues) | |
| then: | |
| response == [eventValue: '2.0', eventCount: '7', transactions: '17', purchaseRevenue: '29.0'] | |
| }</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>@Test | |
| void shouldMapResponseHeadersWithValues() { | |
| // given | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse(); | |
| eventsResponse.setDimensionHeaders(List.of(new Header("date"))); | |
| eventsResponse.setMetricHeaders(List.of( | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| )); | |
| eventsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("2.0", "7", "17", "29.0")) | |
| )); | |
| // when | |
| Map<String, String> response = GoogleAnalyticsResponseUtils.mapHeadersWithValues( | |
| eventsResponse.getMetricHeaders(), | |
| eventsResponse.getRows().get(0).getMetricValues() | |
| ); | |
| // then | |
| assertThat(response).containsExactlyInAnyOrderEntriesOf(Map.of( | |
| "eventValue", "2.0", | |
| "eventCount", "7", | |
| "transactions", "17", | |
| "purchaseRevenue", "29.0" | |
| )); | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Added @Test annotation. Replaced Groovy map constructor with setter calls and helper method. Replaced Spock == with AssertJ containsExactlyInAnyOrderEntriesOf().</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section: Test 2 --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>🧪 Should map response headers with values - headers size greater than values size</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>void 'Should map response headers with values - headers size greater than values size'() { | |
| given: | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date')], | |
| metricHeaders: [new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '2.0')]), | |
| ] | |
| ) | |
| when: | |
| Map<String, String> response = GoogleAnalyticsResponseUtils.mapHeadersWithValues(eventsResponse.metricHeaders, eventsResponse.rows.get(0).metricValues) | |
| then: | |
| response == [eventValue: '2.0', eventCount: '0', transactions: '0', purchaseRevenue: '0.0'] | |
| }</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>@Test | |
| void shouldMapResponseHeadersWithValuesWhenHeadersSizeGreaterThanValuesSize() { | |
| // given | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse(); | |
| eventsResponse.setDimensionHeaders(List.of(new Header("date"))); | |
| eventsResponse.setMetricHeaders(List.of( | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| )); | |
| eventsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("2.0")) | |
| )); | |
| // when | |
| Map<String, String> response = GoogleAnalyticsResponseUtils.mapHeadersWithValues( | |
| eventsResponse.getMetricHeaders(), | |
| eventsResponse.getRows().get(0).getMetricValues() | |
| ); | |
| // then | |
| assertThat(response).containsExactlyInAnyOrderEntriesOf(Map.of( | |
| "eventValue", "2.0", | |
| "eventCount", "0", | |
| "transactions", "0", | |
| "purchaseRevenue", "0.0" | |
| )); | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Added @Test annotation. Converted test name to camelCase. Replaced Groovy construction with Java setters and helper method.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section: Test 3 --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>🧪 Should map response headers with values - values size greater than headers size</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>void 'Should map response headers with values - values size greater than headers size'() { | |
| given: | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date')], | |
| metricHeaders: [new Header(name: 'eventValue'), new Header(name: 'eventCount')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '2.0'), new Value(value: '7'), new Value(value: '17'), new Value(value: '29.0')]), | |
| ] | |
| ) | |
| when: | |
| GoogleAnalyticsResponseUtils.mapHeadersWithValues(eventsResponse.metricHeaders, eventsResponse.rows.get(0).metricValues) | |
| then: | |
| Exception e = thrown(IllegalArgumentException) | |
| e.message == "Headers should have a size greater or equals than values, header size = 2, values size = 4" | |
| }</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>@Test | |
| void shouldThrowExceptionWhenValuesSizeGreaterThanHeadersSize() { | |
| // given | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse(); | |
| eventsResponse.setDimensionHeaders(List.of(new Header("date"))); | |
| eventsResponse.setMetricHeaders(List.of( | |
| new Header("eventValue"), | |
| new Header("eventCount") | |
| )); | |
| eventsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("2.0", "7", "17", "29.0")) | |
| )); | |
| // when/then | |
| assertThatThrownBy(() -> GoogleAnalyticsResponseUtils.mapHeadersWithValues( | |
| eventsResponse.getMetricHeaders(), | |
| eventsResponse.getRows().get(0).getMetricValues() | |
| )) | |
| .isInstanceOf(IllegalArgumentException.class) | |
| .hasMessage("Headers should have a size greater or equals than values, header size = 2, values size = 4"); | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Replaced Spock's thrown() with AssertJ's assertThatThrownBy(). Lambda captures the exception-throwing call. Chains isInstanceOf() and hasMessage() for verification.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section: Test 4 --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>🧪 Should merge batch responses</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>void 'Should merge batch responses'() { | |
| given: | |
| Ga4InsightsApiResponse visitorsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date')], | |
| metricHeaders: [new Header(name: 'totalUsers')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '10')]), | |
| new Row(dimensionValues: [new Value(value: '20230102')], metricValues: [new Value(value: '13')]), | |
| ], | |
| totals: [new Row(metricValues: [new Value(value: '15')])] | |
| ) | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date'), new Header(name: 'eventName')], | |
| metricHeaders: [new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '2.0'), new Value(value: '7'), new Value(value: '17'), new Value(value: '29.0')]), | |
| ], | |
| totals: [new Row(metricValues: [new Value(value: '2.0'), new Value(value: '7'), new Value(value: '17'), new Value(value: '29.0')])] | |
| ) | |
| when: | |
| Ga4InsightsApiResponse mergedResponse = GoogleAnalyticsResponseUtils.mergeBatchResponse(visitorsResponse, eventsResponse) | |
| then: | |
| mergedResponse.dimensionHeaders == [new Header(name: 'date')] | |
| mergedResponse.metricHeaders == [new Header(name: 'totalUsers'), new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')] | |
| mergedResponse.rows == [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '10'), new Value(value: '2.0'), new Value(value: '7'), new Value(value: '17'), new Value(value: '29.0')]), | |
| new Row(dimensionValues: [new Value(value: '20230102')], metricValues: [new Value(value: '13'), new Value(value: '0.0'), new Value(value: '0'), new Value(value: '0'), new Value(value: '0.0')]), | |
| ] | |
| mergedResponse.totals == [ | |
| new Row(metricValues: [new Value(value: '15'), new Value(value: '2.0'), new Value(value: '7'), new Value(value: '17'), new Value(value: '29.0')]) | |
| ] | |
| }</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>@Test | |
| void shouldMergeBatchResponses() { | |
| // given | |
| Ga4InsightsApiResponse visitorsResponse = new Ga4InsightsApiResponse(); | |
| visitorsResponse.setDimensionHeaders(List.of(new Header("date"))); | |
| visitorsResponse.setMetricHeaders(List.of(new Header("totalUsers"))); | |
| visitorsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("10")), | |
| createRow(List.of("20230102"), List.of("13")) | |
| )); | |
| visitorsResponse.setTotals(List.of(createRowWithMetricsOnly(List.of("15")))); | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse(); | |
| eventsResponse.setDimensionHeaders(List.of(new Header("date"), new Header("eventName"))); | |
| eventsResponse.setMetricHeaders(List.of( | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| )); | |
| eventsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("2.0", "7", "17", "29.0")) | |
| )); | |
| eventsResponse.setTotals(List.of(createRowWithMetricsOnly(List.of("2.0", "7", "17", "29.0")))); | |
| // when | |
| Ga4InsightsApiResponse mergedResponse = GoogleAnalyticsResponseUtils.mergeBatchResponse(visitorsResponse, eventsResponse); | |
| // then | |
| assertThat(mergedResponse.getDimensionHeaders()).containsExactly(new Header("date")); | |
| assertThat(mergedResponse.getMetricHeaders()).containsExactly( | |
| new Header("totalUsers"), | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| ); | |
| assertThat(mergedResponse.getRows()).containsExactly( | |
| createRow(List.of("20230101"), List.of("10", "2.0", "7", "17", "29.0")), | |
| createRow(List.of("20230102"), List.of("13", "0.0", "0", "0", "0.0")) | |
| ); | |
| assertThat(mergedResponse.getTotals()).containsExactly( | |
| createRowWithMetricsOnly(List.of("15", "2.0", "7", "17", "29.0")) | |
| ); | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Replaced Groovy property access with Java getters. Used helper methods for cleaner Row construction. Replaced Spock == with AssertJ containsExactly() for list assertions.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section: Test 5 --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>🧪 Should merge batch responses - 0 event</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>void 'Should merge batch responses - 0 event'() { | |
| given: | |
| Ga4InsightsApiResponse visitorsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date')], | |
| metricHeaders: [new Header(name: 'totalUsers')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '10')]), | |
| new Row(dimensionValues: [new Value(value: '20230102')], metricValues: [new Value(value: '13')]), | |
| ], | |
| totals: [new Row(metricValues: [new Value(value: '15')])] | |
| ) | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date'), new Header(name: 'eventName')], | |
| metricHeaders: [new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')], | |
| totals: [new Row()] | |
| ) | |
| when: | |
| Ga4InsightsApiResponse mergedResponse = GoogleAnalyticsResponseUtils.mergeBatchResponse(visitorsResponse, eventsResponse) | |
| then: | |
| mergedResponse.dimensionHeaders == [new Header(name: 'date')] | |
| mergedResponse.metricHeaders == [new Header(name: 'totalUsers'), new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')] | |
| mergedResponse.rows == [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '10'), new Value(value: '0.0'), new Value(value: '0'), new Value(value: '0'), new Value(value: '0.0')]), | |
| new Row(dimensionValues: [new Value(value: '20230102')], metricValues: [new Value(value: '13'), new Value(value: '0.0'), new Value(value: '0'), new Value(value: '0'), new Value(value: '0.0')]), | |
| ] | |
| mergedResponse.totals == [ | |
| new Row(metricValues: [new Value(value: '15'), new Value(value: '0.0'), new Value(value: '0'), new Value(value: '0'), new Value(value: '0.0')]) | |
| ] | |
| }</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>@Test | |
| void shouldMergeBatchResponsesWithZeroEvents() { | |
| // given | |
| Ga4InsightsApiResponse visitorsResponse = new Ga4InsightsApiResponse(); | |
| visitorsResponse.setDimensionHeaders(List.of(new Header("date"))); | |
| visitorsResponse.setMetricHeaders(List.of(new Header("totalUsers"))); | |
| visitorsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("10")), | |
| createRow(List.of("20230102"), List.of("13")) | |
| )); | |
| visitorsResponse.setTotals(List.of(createRowWithMetricsOnly(List.of("15")))); | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse(); | |
| eventsResponse.setDimensionHeaders(List.of(new Header("date"), new Header("eventName"))); | |
| eventsResponse.setMetricHeaders(List.of( | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| )); | |
| eventsResponse.setTotals(List.of(new Row())); | |
| // when | |
| Ga4InsightsApiResponse mergedResponse = GoogleAnalyticsResponseUtils.mergeBatchResponse(visitorsResponse, eventsResponse); | |
| // then | |
| assertThat(mergedResponse.getDimensionHeaders()).containsExactly(new Header("date")); | |
| assertThat(mergedResponse.getMetricHeaders()).containsExactly( | |
| new Header("totalUsers"), | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| ); | |
| assertThat(mergedResponse.getRows()).containsExactly( | |
| createRow(List.of("20230101"), List.of("10", "0.0", "0", "0", "0.0")), | |
| createRow(List.of("20230102"), List.of("13", "0.0", "0", "0", "0.0")) | |
| ); | |
| assertThat(mergedResponse.getTotals()).containsExactly( | |
| createRowWithMetricsOnly(List.of("15", "0.0", "0", "0", "0.0")) | |
| ); | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Test name converted to camelCase (WithZeroEvents). Tests edge case where eventsResponse has no rows (only totals with empty Row). Uses AssertJ containsExactly() for assertions.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section: Test 6 --> | |
| <div class="section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <h2>🧪 Should merge batch responses - all events</h2> | |
| <span class="toggle">▼</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="diff-container"> | |
| <div class="diff-panel left"> | |
| <h3>Spock (Groovy)</h3> | |
| <pre>void 'Should merge batch responses - all events'() { | |
| given: | |
| Ga4InsightsApiResponse visitorsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date')], | |
| metricHeaders: [new Header(name: 'totalUsers')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '10')]), | |
| new Row(dimensionValues: [new Value(value: '20230102')], metricValues: [new Value(value: '13')]), | |
| ], | |
| totals: [new Row(metricValues: [new Value(value: '15')])] | |
| ) | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse( | |
| dimensionHeaders: [new Header(name: 'date'), new Header(name: 'eventName')], | |
| metricHeaders: [new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')], | |
| rows: [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '5.0'), new Value(value: '18'), new Value(value: '36'), new Value(value: '60.0')]), | |
| new Row(dimensionValues: [new Value(value: '20230102')], metricValues: [new Value(value: '5.0'), new Value(value: '13'), new Value(value: '23'), new Value(value: '37.0')]), | |
| ], | |
| totals: [new Row(metricValues: [new Value(value: '10.0'), new Value(value: '31'), new Value(value: '59'), new Value(value: '97.0')])] | |
| ) | |
| when: | |
| Ga4InsightsApiResponse mergedResponse = GoogleAnalyticsResponseUtils.mergeBatchResponse(visitorsResponse, eventsResponse) | |
| then: | |
| mergedResponse.dimensionHeaders == [new Header(name: 'date')] | |
| mergedResponse.metricHeaders == [new Header(name: 'totalUsers'), new Header(name: 'eventValue'), new Header(name: 'eventCount'), new Header(name: 'transactions'), new Header(name: 'purchaseRevenue')] | |
| mergedResponse.rows == [ | |
| new Row(dimensionValues: [new Value(value: '20230101')], metricValues: [new Value(value: '10'), new Value(value: '5.0'), new Value(value: '18'), new Value(value: '36'), new Value(value: '60.0')]), | |
| new Row(dimensionValues: [new Value(value: '20230102')], metricValues: [new Value(value: '13'), new Value(value: '5.0'), new Value(value: '13'), new Value(value: '23'), new Value(value: '37.0')]), | |
| ] | |
| mergedResponse.totals == [ | |
| new Row(metricValues: [new Value(value: '15'), new Value(value: '10.0'), new Value(value: '31'), new Value(value: '59'), new Value(value: '97.0')]) | |
| ] | |
| }</pre> | |
| </div> | |
| <div class="diff-panel right"> | |
| <h3>JUnit 5 (Java)</h3> | |
| <pre>@Test | |
| void shouldMergeBatchResponsesWithAllEvents() { | |
| // given | |
| Ga4InsightsApiResponse visitorsResponse = new Ga4InsightsApiResponse(); | |
| visitorsResponse.setDimensionHeaders(List.of(new Header("date"))); | |
| visitorsResponse.setMetricHeaders(List.of(new Header("totalUsers"))); | |
| visitorsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("10")), | |
| createRow(List.of("20230102"), List.of("13")) | |
| )); | |
| visitorsResponse.setTotals(List.of(createRowWithMetricsOnly(List.of("15")))); | |
| Ga4InsightsApiResponse eventsResponse = new Ga4InsightsApiResponse(); | |
| eventsResponse.setDimensionHeaders(List.of(new Header("date"), new Header("eventName"))); | |
| eventsResponse.setMetricHeaders(List.of( | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| )); | |
| eventsResponse.setRows(List.of( | |
| createRow(List.of("20230101"), List.of("5.0", "18", "36", "60.0")), | |
| createRow(List.of("20230102"), List.of("5.0", "13", "23", "37.0")) | |
| )); | |
| eventsResponse.setTotals(List.of(createRowWithMetricsOnly(List.of("10.0", "31", "59", "97.0")))); | |
| // when | |
| Ga4InsightsApiResponse mergedResponse = GoogleAnalyticsResponseUtils.mergeBatchResponse(visitorsResponse, eventsResponse); | |
| // then | |
| assertThat(mergedResponse.getDimensionHeaders()).containsExactly(new Header("date")); | |
| assertThat(mergedResponse.getMetricHeaders()).containsExactly( | |
| new Header("totalUsers"), | |
| new Header("eventValue"), | |
| new Header("eventCount"), | |
| new Header("transactions"), | |
| new Header("purchaseRevenue") | |
| ); | |
| assertThat(mergedResponse.getRows()).containsExactly( | |
| createRow(List.of("20230101"), List.of("10", "5.0", "18", "36", "60.0")), | |
| createRow(List.of("20230102"), List.of("13", "5.0", "13", "23", "37.0")) | |
| ); | |
| assertThat(mergedResponse.getTotals()).containsExactly( | |
| createRowWithMetricsOnly(List.of("15", "10.0", "31", "59", "97.0")) | |
| ); | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="summary"> | |
| <p><strong>Changes:</strong> Test name converted to camelCase (WithAllEvents). Tests case where every visitor date has matching events. Both responses fully populated - no zero-filling needed.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| function toggleSection(header) { | |
| const content = header.nextElementSibling; | |
| const toggle = header.querySelector('.toggle'); | |
| content.classList.toggle('collapsed'); | |
| toggle.textContent = content.classList.contains('collapsed') ? '▶' : '▼'; | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment