• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-client-gateway / 20091945613

10 Dec 2025 08:19AM UTC coverage: 89.03% (-1.2%) from 90.212%
20091945613

push

github

web-flow
refactor(cache): change Zerion cache keys to be per address only (#2839)

* refactor(cache): change Zerion cache keys to be per address only

Remove chainId from Zerion cache keys for balances, collectibles, and positions.
This changes the caching strategy from per-chain-per-address to per-address only,
allowing data to be shared across chains for the same address.

Cache key changes:
- Before: {chainId}_zerion_balances_{safeAddress}
- After: zerion_balances_{safeAddress}

Similar changes applied to positions and collectibles cache keys.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Prettier

* refactor: remove unused chainId parameter from clear methods

Remove chainId parameter from clearBalances and clearCollectibles methods
in IBalancesApi interface and implementations. After the refactoring to
per-address cache keys, chainId is no longer needed by the API methods:

- ZerionBalancesApi uses address-only cache keys
- SafeBalancesApi uses its instance chainId variable

The repositories still accept chainId (needed to select the correct API)
but no longer pass it to the clear methods.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix tests: Separate unit tests from integration/e2e tests

- Renamed tests requiring real infrastructure (.spec.ts -> .integration.spec.ts or .e2e-spec.ts)
- Updated package.json Jest config to exclude integration and e2e tests from default 'yarn test' run
- Tests requiring Redis, Postgres, or full app bootstrap are now properly categorized
- All 308 unit test suites now pass without requiring external services

Renamed files:
- Redis cache service test -> integration test
- Postgres database module tests (v1 & v2) -> integration tests
- Postgres database service test -> integration test
- Auth decorator test -> e2e test
- Spaces-related controller tests -> e2e tests
- ... (continued)

2835 of 3551 branches covered (79.84%)

Branch coverage included in aggregate %.

4 of 8 new or added lines in 5 files covered. (50.0%)

199 existing lines in 34 files now uncovered.

13616 of 14927 relevant lines covered (91.22%)

564.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.36
/src/modules/queues/datasources/queues-api.module.ts
1
import { IConfigurationService } from '@/config/configuration.service.interface';
94✔
2
import { QueueApiService } from '@/modules/queues/datasources/queues-api.service';
94✔
3
import { IQueuesApiService } from '@/modules/queues/datasources/queues-api.service.interface';
94✔
4
import { QueuesApiShutdownHook } from '@/modules/queues/datasources/queues-api.shutdown.hook';
94✔
5
import { QueueReadiness } from '@/domain/interfaces/queue-readiness.interface';
94✔
6
import { Module } from '@nestjs/common';
94✔
7
import amqp, {
94✔
8
  AmqpConnectionManager,
9
  ChannelWrapper,
10
} from 'amqp-connection-manager';
11
import { Channel } from 'amqplib';
12

13
export interface QueueConsumer {
14
  connection: AmqpConnectionManager;
15
  channel: ChannelWrapper;
16
}
17

18
function queueConsumerFactory(
19
  configurationService: IConfigurationService,
20
): QueueConsumer {
UNCOV
21
  const amqpUrl = configurationService.getOrThrow<string>('amqp.url');
×
22
  const exchangeName =
UNCOV
23
    configurationService.getOrThrow<string>('amqp.exchange.name');
×
24
  const exchangeMode =
UNCOV
25
    configurationService.getOrThrow<string>('amqp.exchange.mode');
×
UNCOV
26
  const queue = configurationService.getOrThrow<string>('amqp.queue');
×
UNCOV
27
  const prefetch = configurationService.getOrThrow<number>('amqp.prefetch');
×
UNCOV
28
  const heartbeatIntervalInSeconds = configurationService.getOrThrow<number>(
×
29
    'amqp.heartbeatIntervalInSeconds',
30
  );
UNCOV
31
  const reconnectTimeInSeconds = configurationService.getOrThrow<number>(
×
32
    'amqp.reconnectTimeInSeconds',
33
  );
34

UNCOV
35
  const connection = amqp.connect(amqpUrl, {
×
36
    heartbeatIntervalInSeconds,
37
    reconnectTimeInSeconds,
38
  });
UNCOV
39
  const channel = connection.createChannel({
×
40
    json: true,
41
    setup: async (ch: Channel) => {
42
      await ch.assertExchange(exchangeName, exchangeMode, { durable: true });
×
43
      await ch.assertQueue(queue, { durable: true });
×
44
      // Note: Using consumer (not channel) prefetch (https://www.rabbitmq.com/docs/consumer-prefetch)
45
      await ch.prefetch(prefetch);
×
46
      await ch.bindQueue(queue, exchangeName, '');
×
47
    },
48
  });
UNCOV
49
  return { connection, channel };
×
50
}
51

52
@Module({
53
  providers: [
54
    {
55
      provide: 'QueueConsumer',
56
      useFactory: queueConsumerFactory,
57
      inject: [IConfigurationService],
58
    },
59
    { provide: IQueuesApiService, useClass: QueueApiService },
60
    { provide: QueueReadiness, useExisting: IQueuesApiService },
61
    QueuesApiShutdownHook,
62
  ],
63
  exports: [IQueuesApiService, QueueReadiness],
64
})
65
export class QueuesApiModule {}
94✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc