Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save musketyr/8a805aadc413b11e1e79e61ea2e9bdcf to your computer and use it in GitHub Desktop.

Select an option

Save musketyr/8a805aadc413b11e1e79e61ea2e9bdcf to your computer and use it in GitHub Desktop.
Spock to JUnit 5 Migration Diff - sc185914 - RoiDataSynchronizerServiceSpec
<!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: RoiDataSynchronizerServiceSpec</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: RoiDataSynchronizerServiceSpec</h1>
<p style="text-align: center; color: #666; margin-bottom: 20px;">Story: <a href="https://app.shortcut.com/agorapulse/story/185914">sc185914</a> | PR: <a href="https://github.com/agorapulse/platform/pull/72756">#72756</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.data.synchronizer
import agorapulse.organization.client.internal.ManagerClient
import agorapulse.organization.client.internal.OrganizationClient
import agorapulse.organization.client.model.ManagerSummary
import agorapulse.organization.client.model.OrganizationManagerSummary
import agorapulse.organization.client.model.OrganizationRole
import agorapulse.organization.client.model.OrganizationSummary
import agorapulse.organization.subscription.client.SubscriptionContextService
import agorapulse.organization.subscription.client.context.SubscriptionContext
import agorapulse.organization.subscription.client.context.SubscriptionType
import agorapulse.roi.data.entity.googleanalyticsaccount.GoogleAnalyticsAccountEntity
import agorapulse.roi.data.entity.googleanalyticsdashboard.GoogleAnalyticsDashboardEntity
import agorapulse.roi.data.service.googleanalyticsdashboard.GoogleAnalyticsDashboardService
import agorapulse.roi.internal.client.RoiClient
import com.amazonaws.services.lambda.runtime.events.SQSEvent
import spock.lang.Specification</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>package agorapulse.roi.data.synchronizer;
import agorapulse.organization.client.internal.ManagerClient;
import agorapulse.organization.client.internal.OrganizationClient;
import agorapulse.organization.client.model.ManagerSummary;
import agorapulse.organization.client.model.OrganizationManagerSummary;
import agorapulse.organization.client.model.OrganizationRole;
import agorapulse.organization.client.model.OrganizationSummary;
import agorapulse.organization.subscription.client.SubscriptionContextService;
import agorapulse.organization.subscription.client.context.SubscriptionContext;
import agorapulse.organization.subscription.client.context.SubscriptionType;
import agorapulse.roi.data.entity.googleanalyticsaccount.GoogleAnalyticsAccountEntity;
import agorapulse.roi.data.entity.googleanalyticsdashboard.GoogleAnalyticsDashboardEntity;
import agorapulse.roi.data.service.googleanalyticsdashboard.GoogleAnalyticsDashboardService;
import agorapulse.roi.internal.client.RoiClient;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Replaced Spock imports with JUnit 5 + Mockito imports. Added MockitoExtension, @Mock, @BeforeEach, @Test annotations. Added static imports for Mockito methods.</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 RoiDataSynchronizerServiceSpec extends Specification {
RoiDataSynchronizerService service
GoogleAnalyticsDashboardService googleAnalyticsDashboardService = Mock(GoogleAnalyticsDashboardService)
ManagerClient managerClient = Mock(ManagerClient)
OrganizationClient organizationClient = Mock(OrganizationClient)
RoiClient roiClient = Mock(RoiClient)
SubscriptionContextService subscriptionContextService = Mock(SubscriptionContextService)</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@ExtendWith(MockitoExtension.class)
class RoiDataSynchronizerServiceTest {
@Mock
private GoogleAnalyticsDashboardService googleAnalyticsDashboardService;
@Mock
private ManagerClient managerClient;
@Mock
private OrganizationClient organizationClient;
@Mock
private RoiClient roiClient;
@Mock
private SubscriptionContextService subscriptionContextService;
private RoiDataSynchronizerService service;</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Replaced Spock's extends Specification with @ExtendWith(MockitoExtension.class). Converted Mock(Class) to @Mock annotations. Added proper Java field declarations with semicolons.</p>
</div>
</div>
</div>
<!-- Section: Setup -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🔧 Setup</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 setup() {
service = new RoiDataSynchronizerService(
googleAnalyticsDashboardService, managerClient, organizationClient, roiClient, subscriptionContextService
)
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@BeforeEach
void setup() {
service = new RoiDataSynchronizerService(
googleAnalyticsDashboardService,
managerClient,
organizationClient,
roiClient,
subscriptionContextService
);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Added @BeforeEach annotation (JUnit 5 equivalent of Spock's setup()). Added semicolon and improved formatting.</p>
</div>
</div>
</div>
<!-- Test 1: should not send any data if the dashboard does not exist -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🧪 should not send any data if the dashboard does not exist</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 not send any data if the dashboard does not exist'() {
given:
String dashboardId = '1'
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage()
sqsMessage.body = dashboardId
SQSEvent sqsEvent = new SQSEvent(records: [sqsMessage])
when:
service.handleSynchronizations(sqsEvent)
then:
1 * googleAnalyticsDashboardService.getDashboard(dashboardId.toLong()) >> null
0 * _
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@Test
void should_not_send_any_data_if_the_dashboard_does_not_exist() {
// given
String dashboardId = "1";
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody(dashboardId);
SQSEvent sqsEvent = new SQSEvent();
sqsEvent.setRecords(List.of(sqsMessage));
when(googleAnalyticsDashboardService.getDashboard(1L)).thenReturn(null);
// when
service.handleSynchronizations(sqsEvent);
// then
verify(googleAnalyticsDashboardService).getDashboard(1L);
verifyNoMoreInteractions(googleAnalyticsDashboardService);
verifyNoInteractions(subscriptionContextService, organizationClient, managerClient, roiClient);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Added @Test annotation. Replaced Groovy property access with Java setters. Converted Spock stubbing (1 * ... >> null) to Mockito when().thenReturn() + verify(). Replaced 0 * _ with verifyNoMoreInteractions() and verifyNoInteractions().</p>
</div>
</div>
</div>
<!-- Test 2: should not send any data if there is no active subscription -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🧪 should not send any data if there is no active subscription</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 not send any data if there is no active subscription'() {
given:
String dashboardId = '1'
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage()
sqsMessage.body = dashboardId
SQSEvent sqsEvent = new SQSEvent(records: [sqsMessage])
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder().organizationUid('organization_1').workspaceUid('workspace_1').token('token').build())
.build()
when:
service.handleSynchronizations(sqsEvent)
then:
1 * googleAnalyticsDashboardService.getDashboard(1L) >> dashboard
1 * subscriptionContextService.getSubscriptionContextForRoi(1L) >> Optional.of(SubscriptionContext.builder().type(SubscriptionType.NONE).build())
0 * _
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@Test
void should_not_send_any_data_if_there_is_no_active_subscription() {
// given
String dashboardId = "1";
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody(dashboardId);
SQSEvent sqsEvent = new SQSEvent();
sqsEvent.setRecords(List.of(sqsMessage));
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder()
.organizationUid("organization_1")
.workspaceUid("workspace_1")
.token("token")
.build())
.build();
when(googleAnalyticsDashboardService.getDashboard(1L)).thenReturn(dashboard);
when(subscriptionContextService.getSubscriptionContextForRoi(1L))
.thenReturn(Optional.of(SubscriptionContext.builder().type(SubscriptionType.NONE).build()));
// when
service.handleSynchronizations(sqsEvent);
// then
verify(googleAnalyticsDashboardService).getDashboard(1L);
verify(subscriptionContextService).getSubscriptionContextForRoi(1L);
verifyNoMoreInteractions(googleAnalyticsDashboardService, subscriptionContextService);
verifyNoInteractions(organizationClient, managerClient, roiClient);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Converted Groovy single quotes to Java double quotes. Replaced Spock stubbing with Mockito when().thenReturn(). Split verify calls for each interaction. Used verifyNoMoreInteractions() for called mocks and verifyNoInteractions() for unused mocks.</p>
</div>
</div>
</div>
<!-- Test 3: should use cache to check active subscriptions -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🧪 should use cache to check active subscriptions (only 1 call to subscription context)</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 use cache to check active subscriptions (only 1 call to subscription context)'() {
given:
String dashboardId = '1,2,3'
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage()
sqsMessage.body = dashboardId
SQSEvent sqsEvent = new SQSEvent(records: [sqsMessage])
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder().organizationUid('organization_1').workspaceUid('workspace_1').token('token').build())
.build()
when:
service.handleSynchronizations(sqsEvent)
then:
1 * googleAnalyticsDashboardService.getDashboard(1L) >> dashboard
1 * googleAnalyticsDashboardService.getDashboard(2L) >> dashboard
1 * googleAnalyticsDashboardService.getDashboard(3L) >> dashboard
1 * subscriptionContextService.getSubscriptionContextForRoi(1L) >> Optional.of(SubscriptionContext.builder().type(SubscriptionType.NONE).build())
0 * _
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@Test
void should_use_cache_to_check_active_subscriptions_only_1_call_to_subscription_context() {
// given
String dashboardId = "1,2,3";
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody(dashboardId);
SQSEvent sqsEvent = new SQSEvent();
sqsEvent.setRecords(List.of(sqsMessage));
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder()
.organizationUid("organization_1")
.workspaceUid("workspace_1")
.token("token")
.build())
.build();
when(googleAnalyticsDashboardService.getDashboard(1L)).thenReturn(dashboard);
when(googleAnalyticsDashboardService.getDashboard(2L)).thenReturn(dashboard);
when(googleAnalyticsDashboardService.getDashboard(3L)).thenReturn(dashboard);
when(subscriptionContextService.getSubscriptionContextForRoi(1L))
.thenReturn(Optional.of(SubscriptionContext.builder().type(SubscriptionType.NONE).build()));
// when
service.handleSynchronizations(sqsEvent);
// then
verify(googleAnalyticsDashboardService).getDashboard(1L);
verify(googleAnalyticsDashboardService).getDashboard(2L);
verify(googleAnalyticsDashboardService).getDashboard(3L);
verify(subscriptionContextService).getSubscriptionContextForRoi(1L);
verifyNoMoreInteractions(googleAnalyticsDashboardService, subscriptionContextService);
verifyNoInteractions(organizationClient, managerClient, roiClient);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Tests caching behavior - subscription context is called only once for 3 dashboards from same organization. Spock's multiple 1 * calls converted to separate when().thenReturn() stubs and verify() calls.</p>
</div>
</div>
</div>
<!-- Test 4: should not send any data if there is no owner -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🧪 should not send any data if there is no owner</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 not send any data if there is no owner'() {
given:
String dashboardId = '1'
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage()
sqsMessage.body = dashboardId
SQSEvent sqsEvent = new SQSEvent(records: [sqsMessage])
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder().organizationUid('organization_1').workspaceUid('workspace_1').token('token').build())
.build()
when:
service.handleSynchronizations(sqsEvent)
then:
1 * googleAnalyticsDashboardService.getDashboard(1L) >> dashboard
1 * subscriptionContextService.getSubscriptionContextForRoi(1L) >> Optional.of(SubscriptionContext.builder().type(SubscriptionType.PAID).build())
1 * organizationClient.getOrganization(1L) >> new OrganizationSummary(
organizationManagers: []
)
0 * _
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@Test
void should_not_send_any_data_if_there_is_no_owner() {
// given
String dashboardId = "1";
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody(dashboardId);
SQSEvent sqsEvent = new SQSEvent();
sqsEvent.setRecords(List.of(sqsMessage));
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder()
.organizationUid("organization_1")
.workspaceUid("workspace_1")
.token("token")
.build())
.build();
OrganizationSummary organizationSummary = new OrganizationSummary();
organizationSummary.setOrganizationManagers(Collections.emptyList());
when(googleAnalyticsDashboardService.getDashboard(1L)).thenReturn(dashboard);
when(subscriptionContextService.getSubscriptionContextForRoi(1L))
.thenReturn(Optional.of(SubscriptionContext.builder().type(SubscriptionType.PAID).build()));
when(organizationClient.getOrganization(1L)).thenReturn(organizationSummary);
// when
service.handleSynchronizations(sqsEvent);
// then
verify(googleAnalyticsDashboardService).getDashboard(1L);
verify(subscriptionContextService).getSubscriptionContextForRoi(1L);
verify(organizationClient).getOrganization(1L);
verifyNoMoreInteractions(googleAnalyticsDashboardService, subscriptionContextService, organizationClient);
verifyNoInteractions(managerClient, roiClient);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Replaced Groovy constructor with named parameters (organizationManagers: []) with Java setter (setOrganizationManagers(Collections.emptyList())). Now using PAID subscription type which has active subscription, but no owner stops the flow.</p>
</div>
</div>
</div>
<!-- Test 5: should use cache to check owner -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🧪 should use cache to check owner (only 1 call to organizationClient)</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 use cache to check owner (only 1 call to organizationClient)'() {
given:
String dashboardId = '1,2,3'
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage()
sqsMessage.body = dashboardId
SQSEvent sqsEvent = new SQSEvent(records: [sqsMessage])
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder().organizationUid('organization_1').workspaceUid('workspace_1').token('token').build())
.build()
when:
service.handleSynchronizations(sqsEvent)
then:
1 * googleAnalyticsDashboardService.getDashboard(1L) >> dashboard
1 * googleAnalyticsDashboardService.getDashboard(2L) >> dashboard
1 * googleAnalyticsDashboardService.getDashboard(3L) >> dashboard
1 * subscriptionContextService.getSubscriptionContextForRoi(1L) >> Optional.of(SubscriptionContext.builder().type(SubscriptionType.PAID).build())
1 * organizationClient.getOrganization(1L) >> new OrganizationSummary(
organizationManagers: []
)
0 * _
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@Test
void should_use_cache_to_check_owner_only_1_call_to_organizationClient() {
// given
String dashboardId = "1,2,3";
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody(dashboardId);
SQSEvent sqsEvent = new SQSEvent();
sqsEvent.setRecords(List.of(sqsMessage));
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder()
.organizationUid("organization_1")
.workspaceUid("workspace_1")
.token("token")
.build())
.build();
OrganizationSummary organizationSummary = new OrganizationSummary();
organizationSummary.setOrganizationManagers(Collections.emptyList());
when(googleAnalyticsDashboardService.getDashboard(1L)).thenReturn(dashboard);
when(googleAnalyticsDashboardService.getDashboard(2L)).thenReturn(dashboard);
when(googleAnalyticsDashboardService.getDashboard(3L)).thenReturn(dashboard);
when(subscriptionContextService.getSubscriptionContextForRoi(1L))
.thenReturn(Optional.of(SubscriptionContext.builder().type(SubscriptionType.PAID).build()));
when(organizationClient.getOrganization(1L)).thenReturn(organizationSummary);
// when
service.handleSynchronizations(sqsEvent);
// then
verify(googleAnalyticsDashboardService).getDashboard(1L);
verify(googleAnalyticsDashboardService).getDashboard(2L);
verify(googleAnalyticsDashboardService).getDashboard(3L);
verify(subscriptionContextService).getSubscriptionContextForRoi(1L);
verify(organizationClient).getOrganization(1L);
verifyNoMoreInteractions(googleAnalyticsDashboardService, subscriptionContextService, organizationClient);
verifyNoInteractions(managerClient, roiClient);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Tests owner caching - organization client called only once for 3 dashboards from same organization. Same pattern as subscription caching test.</p>
</div>
</div>
</div>
<!-- Test 6: should not send any data if there is no token -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🧪 should not send any data if there is no token</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 not send any data if there is no token'() {
given:
String dashboardId = '1'
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage()
sqsMessage.body = dashboardId
SQSEvent sqsEvent = new SQSEvent(records: [sqsMessage])
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder().organizationUid('organization_1').workspaceUid('workspace_1').token(null).build())
.build()
when:
service.handleSynchronizations(sqsEvent)
then:
1 * googleAnalyticsDashboardService.getDashboard(dashboardId.toLong()) >> dashboard
0 * _
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@Test
void should_not_send_any_data_if_there_is_no_token() {
// given
String dashboardId = "1";
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody(dashboardId);
SQSEvent sqsEvent = new SQSEvent();
sqsEvent.setRecords(List.of(sqsMessage));
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder()
.organizationUid("organization_1")
.workspaceUid("workspace_1")
.token(null)
.build())
.build();
when(googleAnalyticsDashboardService.getDashboard(1L)).thenReturn(dashboard);
// when
service.handleSynchronizations(sqsEvent);
// then
verify(googleAnalyticsDashboardService).getDashboard(1L);
verifyNoMoreInteractions(googleAnalyticsDashboardService);
verifyNoInteractions(subscriptionContextService, organizationClient, managerClient, roiClient);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Tests early exit when token is null. Dashboard exists but has no token, so subscription check is skipped entirely. Converted dashboardId.toLong() to literal 1L.</p>
</div>
</div>
</div>
<!-- Test 7: should send data if there is token and owner -->
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<h2>🧪 should send data if there is token and owner</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 send data if there is token and owner'() {
given:
String dashboardId = '1'
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage()
sqsMessage.body = dashboardId
SQSEvent sqsEvent = new SQSEvent(records: [sqsMessage])
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder().organizationUid('organization_1').token('token').workspaceUid('workspace_1').build())
.build()
when:
service.handleSynchronizations(sqsEvent)
then:
1 * googleAnalyticsDashboardService.getDashboard(dashboardId.toLong()) >> dashboard
1 * subscriptionContextService.getSubscriptionContextForRoi(1L) >> Optional.of(SubscriptionContext.builder().type(SubscriptionType.PAID).build())
1 * organizationClient.getOrganization(1L) >> new OrganizationSummary(
organizationManagers: [new OrganizationManagerSummary(role: OrganizationRole.owner, managerId: 1)]
)
1 * managerClient.getManager(1) >> new ManagerSummary(identityId: 'identityId')
1 * roiClient.buildRoi(_)
0 * _
}</pre>
</div>
<div class="diff-panel right">
<h3>JUnit 5 (Java)</h3>
<pre>@Test
void should_send_data_if_there_is_token_and_owner() {
// given
String dashboardId = "1";
SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage();
sqsMessage.setBody(dashboardId);
SQSEvent sqsEvent = new SQSEvent();
sqsEvent.setRecords(List.of(sqsMessage));
GoogleAnalyticsDashboardEntity dashboard = GoogleAnalyticsDashboardEntity.builder()
.googleAnalyticsAccount(GoogleAnalyticsAccountEntity.builder()
.organizationUid("organization_1")
.token("token")
.workspaceUid("workspace_1")
.build())
.build();
OrganizationManagerSummary ownerManager = new OrganizationManagerSummary();
ownerManager.setRole(OrganizationRole.owner);
ownerManager.setManagerId(1L);
OrganizationSummary organizationSummary = new OrganizationSummary();
organizationSummary.setOrganizationManagers(List.of(ownerManager));
ManagerSummary managerSummary = new ManagerSummary();
managerSummary.setIdentityId("identityId");
when(googleAnalyticsDashboardService.getDashboard(1L)).thenReturn(dashboard);
when(subscriptionContextService.getSubscriptionContextForRoi(1L))
.thenReturn(Optional.of(SubscriptionContext.builder().type(SubscriptionType.PAID).build()));
when(organizationClient.getOrganization(1L)).thenReturn(organizationSummary);
when(managerClient.getManager(1L)).thenReturn(managerSummary);
// when
service.handleSynchronizations(sqsEvent);
// then
verify(googleAnalyticsDashboardService).getDashboard(1L);
verify(subscriptionContextService).getSubscriptionContextForRoi(1L);
verify(organizationClient).getOrganization(1L);
verify(managerClient).getManager(1L);
verify(roiClient).buildRoi(any());
verifyNoMoreInteractions(googleAnalyticsDashboardService, subscriptionContextService, organizationClient, managerClient, roiClient);
}</pre>
</div>
</div>
<div class="summary">
<p><strong>Changes:</strong> Happy path test - full flow through to roiClient.buildRoi(). Groovy constructor with named params converted to Java setters. Spock's _ wildcard matcher converted to Mockito's any(). All mocks verified with verifyNoMoreInteractions().</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