mirror of
https://github.com/gradle/actions.git
synced 2026-06-28 14:28:28 +00:00
cbc6f4a545
Render the caching report from structured CacheReport data using a single unified layout, replacing the three divergent code paths (NoOp / basic / enhanced) that previously each rendered their own markdown. Every variant now shares one skeleton: a section heading (`#### <icon> Gradle Caching — <Provider> (<status>)`), a status line, an integrated provider note, and an expandable cache-entry-details section. The Enhanced/Basic provider note is woven into the report under the heading and is now shown unconditionally (no longer gated on license acceptance). The two disabled variants (explicitly disabled, and skipped due to a pre-existing Gradle User Home) render as compact callouts with no expandable section. - caching-report.ts: new central renderer + all framing copy. - cache-service.ts: CacheReport/CacheEntryReport/status types; save() returns CacheReport. - cache-service-loader.ts: NoOp returns a CacheReport, drop LicenseWarningCacheService, add getProviderNote(). - cache-service-basic.ts: build a CacheReport, so basic caching now also gets expandable entry details. - job-summary.ts / setup-gradle.ts: thread CacheReport + ProviderNote. - configuration.ts: remove now-unused isCacheLicenseAccepted(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
239 lines
8.5 KiB
TypeScript
239 lines
8.5 KiB
TypeScript
import {describe, expect, it, jest, beforeEach} from '@jest/globals'
|
|
|
|
// Mock @actions/cache
|
|
const mockRestoreCache = jest.fn<(paths: string[], primaryKey: string, restoreKeys?: string[]) => Promise<string | undefined>>()
|
|
const mockSaveCache = jest.fn<(paths: string[], key: string) => Promise<number>>()
|
|
jest.unstable_mockModule('@actions/cache', () => ({
|
|
restoreCache: mockRestoreCache,
|
|
saveCache: mockSaveCache
|
|
}))
|
|
|
|
// Mock @actions/core
|
|
const mockInfo = jest.fn<(message: string) => void>()
|
|
const mockWarning = jest.fn<(message: string) => void>()
|
|
const mockSaveState = jest.fn<(name: string, value: string) => void>()
|
|
const mockGetState = jest.fn<(name: string) => string>()
|
|
jest.unstable_mockModule('@actions/core', () => ({
|
|
info: mockInfo,
|
|
warning: mockWarning,
|
|
saveState: mockSaveState,
|
|
getState: mockGetState
|
|
}))
|
|
|
|
// Mock @actions/glob
|
|
const mockHashFiles = jest.fn<(pattern: string) => Promise<string>>()
|
|
jest.unstable_mockModule('@actions/glob', () => ({
|
|
hashFiles: mockHashFiles
|
|
}))
|
|
|
|
const {BasicCacheService} = await import('../../src/cache-service-basic')
|
|
|
|
const HASH = 'abc123def456'
|
|
const PRIMARY_KEY = `setup-java-Linux-${process.arch}-gradle-${HASH}`
|
|
|
|
describe('BasicCacheService', () => {
|
|
let service: InstanceType<typeof BasicCacheService>
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
service = new BasicCacheService()
|
|
process.env['RUNNER_OS'] = 'Linux'
|
|
mockHashFiles.mockResolvedValue(HASH)
|
|
})
|
|
|
|
describe('restore', () => {
|
|
it('restores cache without restoreKeys and saves both keys to state', async () => {
|
|
mockRestoreCache.mockResolvedValue(PRIMARY_KEY)
|
|
|
|
await service.restore('/home/.gradle', {
|
|
disabled: false,
|
|
readOnly: false,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
// No restoreKeys parameter — exact match only (setup-java#269)
|
|
expect(mockRestoreCache).toHaveBeenCalledWith(
|
|
['/home/.gradle/caches', '/home/.gradle/wrapper'],
|
|
PRIMARY_KEY
|
|
)
|
|
expect(mockSaveState).toHaveBeenCalledWith('BASIC_CACHE_PRIMARY_KEY', PRIMARY_KEY)
|
|
expect(mockSaveState).toHaveBeenCalledWith('BASIC_CACHE_RESTORED_KEY', PRIMARY_KEY)
|
|
})
|
|
|
|
it('saves primary key to state even on cache miss', async () => {
|
|
mockRestoreCache.mockResolvedValue(undefined)
|
|
|
|
await service.restore('/home/.gradle', {
|
|
disabled: false,
|
|
readOnly: false,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
expect(mockSaveState).toHaveBeenCalledWith('BASIC_CACHE_PRIMARY_KEY', PRIMARY_KEY)
|
|
expect(mockSaveState).not.toHaveBeenCalledWith('BASIC_CACHE_RESTORED_KEY', expect.anything())
|
|
expect(mockInfo).toHaveBeenCalledWith(
|
|
expect.stringContaining('did not find')
|
|
)
|
|
})
|
|
|
|
it('warns on restore failure instead of throwing', async () => {
|
|
mockRestoreCache.mockRejectedValue(new Error('Network error'))
|
|
|
|
await service.restore('/home/.gradle', {
|
|
disabled: false,
|
|
readOnly: false,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
expect(mockWarning).toHaveBeenCalledWith(
|
|
expect.stringContaining('failed to restore')
|
|
)
|
|
})
|
|
|
|
it('throws when no build files are found', async () => {
|
|
mockHashFiles.mockResolvedValue('')
|
|
|
|
await expect(
|
|
service.restore('/home/.gradle', {
|
|
disabled: false,
|
|
readOnly: false,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
).rejects.toThrow('No file in')
|
|
})
|
|
})
|
|
|
|
describe('save', () => {
|
|
it('reports readOnly with restored key when cache was hit', async () => {
|
|
mockGetState.mockReturnValue(PRIMARY_KEY)
|
|
const report = await service.save('/home/.gradle', [], {
|
|
disabled: false,
|
|
readOnly: true,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
expect(mockSaveCache).not.toHaveBeenCalled()
|
|
expect(report.status).toBe('read-only')
|
|
expect(report.entries[0].restoredKey).toBe(PRIMARY_KEY)
|
|
})
|
|
|
|
it('reports readOnly with no restore when cache was missed', async () => {
|
|
mockGetState.mockReturnValue('')
|
|
const report = await service.save('/home/.gradle', [], {
|
|
disabled: false,
|
|
readOnly: true,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
expect(mockSaveCache).not.toHaveBeenCalled()
|
|
expect(report.status).toBe('read-only')
|
|
expect(report.entries[0].restoredKey).toBeUndefined()
|
|
expect(report.entries[0].restoredOutcome).toContain('not restored')
|
|
})
|
|
|
|
it('skips save when restored key equals primary key', async () => {
|
|
mockGetState.mockImplementation((name: string) => {
|
|
if (name === 'BASIC_CACHE_PRIMARY_KEY') return PRIMARY_KEY
|
|
if (name === 'BASIC_CACHE_RESTORED_KEY') return PRIMARY_KEY
|
|
return ''
|
|
})
|
|
|
|
const report = await service.save('/home/.gradle', [], {
|
|
disabled: false,
|
|
readOnly: false,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
expect(mockSaveCache).not.toHaveBeenCalled()
|
|
expect(report.status).toBe('enabled')
|
|
expect(report.entries[0].savedOutcome).toContain('already exists')
|
|
})
|
|
|
|
it('saves cache and returns report on success', async () => {
|
|
mockGetState.mockImplementation((name: string) => {
|
|
if (name === 'BASIC_CACHE_PRIMARY_KEY') return PRIMARY_KEY
|
|
return ''
|
|
})
|
|
mockSaveCache.mockResolvedValue(0)
|
|
|
|
const report = await service.save('/home/.gradle', [], {
|
|
disabled: false,
|
|
readOnly: false,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
expect(mockSaveCache).toHaveBeenCalledWith(
|
|
['/home/.gradle/caches', '/home/.gradle/wrapper'],
|
|
PRIMARY_KEY
|
|
)
|
|
expect(report.status).toBe('enabled')
|
|
expect(report.entries[0].savedKey).toBe(PRIMARY_KEY)
|
|
expect(report.entries[0].savedOutcome).toBe('(Entry saved)')
|
|
})
|
|
|
|
it('warns on save failure instead of throwing', async () => {
|
|
mockGetState.mockImplementation((name: string) => {
|
|
if (name === 'BASIC_CACHE_PRIMARY_KEY') return PRIMARY_KEY
|
|
return ''
|
|
})
|
|
mockSaveCache.mockRejectedValue(new Error('Storage full'))
|
|
|
|
const report = await service.save('/home/.gradle', [], {
|
|
disabled: false,
|
|
readOnly: false,
|
|
writeOnly: false,
|
|
overwriteExisting: false,
|
|
strictMatch: false,
|
|
cleanup: 'never',
|
|
includes: [],
|
|
excludes: []
|
|
})
|
|
|
|
expect(mockWarning).toHaveBeenCalledWith(
|
|
expect.stringContaining('failed to save')
|
|
)
|
|
expect(report.entries[0].savedOutcome).toContain('not saved')
|
|
})
|
|
})
|
|
})
|