• 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

35.71
/src/datasources/cache/cache.module.ts
1
import { Global, Module } from '@nestjs/common';
98✔
2
import { createClient } from 'redis';
98✔
3
import { IConfigurationService } from '@/config/configuration.service.interface';
98✔
4
import { CacheService } from '@/datasources/cache/cache.service.interface';
98✔
5
import { RedisCacheService } from '@/datasources/cache/redis.cache.service';
98✔
6
import { CacheReadiness } from '@/domain/interfaces/cache-readiness.interface';
98✔
7
import { ILoggingService, LoggingService } from '@/logging/logging.interface';
98✔
8
import { CacheKeyPrefix } from '@/datasources/cache/constants';
98✔
9
import { LogType } from '@/domain/common/entities/log-type.entity';
98✔
10

11
export type RedisClientType = ReturnType<typeof createClient>;
12

13
async function redisClientFactory(
14
  configurationService: IConfigurationService,
15
  loggingService: ILoggingService,
16
): Promise<RedisClientType> {
UNCOV
17
  const redisUser = configurationService.get<string>('redis.user');
×
UNCOV
18
  const redisPass = configurationService.get<string>('redis.pass');
×
UNCOV
19
  const redisHost = configurationService.getOrThrow<string>('redis.host');
×
UNCOV
20
  const redisPort = configurationService.getOrThrow<string>('redis.port');
×
UNCOV
21
  const redisConnectTimeout = configurationService.getOrThrow<number>(
×
22
    'redis.connectTimeout',
23
  );
24
  const redisKeepAlive =
UNCOV
25
    configurationService.getOrThrow<number>('redis.keepAlive');
×
UNCOV
26
  const redisDisableOfflineQueue = configurationService.getOrThrow<boolean>(
×
27
    'redis.disableOfflineQueue',
28
  );
UNCOV
29
  const client: RedisClientType = createClient({
×
30
    socket: {
31
      host: redisHost,
32
      port: Number(redisPort),
33
      keepAlive: redisKeepAlive,
34
      connectTimeout: redisConnectTimeout,
35
    },
36
    username: redisUser,
37
    password: redisPass,
38
    disableOfflineQueue: redisDisableOfflineQueue,
39
  });
UNCOV
40
  client.on('error', (err) =>
×
41
    loggingService.error({
×
42
      type: LogType.CacheError,
43
      source: 'CacheModule',
44
      event: err.code ?? err.message,
×
45
    }),
46
  );
UNCOV
47
  client.on('reconnecting', () =>
×
48
    loggingService.warn({
×
49
      type: LogType.CacheEvent,
50
      source: 'CacheModule',
51
      event: 'Reconnecting to Redis',
52
    }),
53
  );
UNCOV
54
  client.on('end', () =>
×
UNCOV
55
    loggingService.warn({
×
56
      type: LogType.CacheEvent,
57
      source: 'CacheModule',
58
      event: 'Redis connection closed',
59
    }),
60
  );
UNCOV
61
  await client.connect();
×
UNCOV
62
  return client;
×
63
}
64

65
@Global()
66
@Module({
67
  providers: [
68
    {
69
      provide: 'RedisClient',
70
      useFactory: redisClientFactory,
71
      inject: [IConfigurationService, LoggingService],
72
    },
73
    { provide: CacheService, useClass: RedisCacheService },
74
    { provide: CacheReadiness, useExisting: CacheService },
75
    { provide: CacheKeyPrefix, useValue: '' },
76
  ],
77
  exports: [CacheService, CacheReadiness],
78
})
79
export class CacheModule {}
98✔
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

© 2025 Coveralls, Inc