| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  | import * as core from '@actions/core'; | 
					
						
							|  |  |  | import * as cache from '@actions/cache'; | 
					
						
							|  |  |  | import * as exec from '@actions/exec'; | 
					
						
							|  |  |  | import {run} from '../src/cache-save'; | 
					
						
							|  |  |  | import {State} from '../src/cache-distributions/cache-distributor'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('run', () => { | 
					
						
							|  |  |  |   const pipFileLockHash = | 
					
						
							| 
									
										
										
										
											2022-04-04 16:12:24 +03:00
										 |  |  |     'd1dd6218299d8a6db5fc2001d988b34a8b31f1e9d0bb4534d377dde7c19f64b3'; | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  |   const requirementsHash = | 
					
						
							|  |  |  |     'd8110e0006d7fb5ee76365d565eef9d37df1d11598b912d3eb66d398d57a1121'; | 
					
						
							|  |  |  |   const requirementsLinuxHash = | 
					
						
							|  |  |  |     '2d0ff7f46b0e120e3d3294db65768b474934242637b9899b873e6283dfd16d7c'; | 
					
						
							| 
									
										
										
										
											2022-03-06 20:30:49 -06:00
										 |  |  |   const poetryLockHash = | 
					
						
							|  |  |  |     '571bf984f8d210e6a97f854e479fdd4a2b5af67b5fdac109ec337a0ea16e7836'; | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // core spy
 | 
					
						
							|  |  |  |   let infoSpy: jest.SpyInstance; | 
					
						
							|  |  |  |   let warningSpy: jest.SpyInstance; | 
					
						
							|  |  |  |   let debugSpy: jest.SpyInstance; | 
					
						
							| 
									
										
											  
											
												Use correct Poetry config when collecting Poetry projects (#447)
* Use correct Poetry config when collecting Poetry projects
When collecting Poetry projects for caching, a '**/poetry.lock' glob is
used.  However, in order to process the Poetry configuration, the
"poetry" command is run from the repo's root directory; this causes
Poetry to return an invalid configuration when there is a Poetry project
inside an inner directory.
Instead of running a single Poetry command, glob for the same pattern,
and run a Poetry command for every discovered project.
* Fix typo: saveSatetSpy -> saveStateSpy
* poetry: Support same virtualenv appearing in multiple projects
* Add nested Poetry projects test
* poetry: Set up environment for each project individually
* tests/cache-restore: Do not look for dependency files outside `data`
When the default dependency path is used for cache distributors, they
are looking for the dependency file in the project's root (including the
source code), which leads to tests taking a significant amount of time,
especially on Windows runners.  We thus hit sporadic test failures.
Change the test cases such that dependency files are always searched for
inside of `__tests__/data`, ignoring the rest of the project.
* poetry: Simplify `virtualenvs.in-project` boolean check
* README: Explain that poetry might create multiple caches
* poetry: Run `poetry env use` only after cache is loaded
The virtualenv cache might contain invalid entries, such as virtualenvs
built in previous, buggy versions of this action.  The `poetry env use`
command will recreate virtualenvs in case they are invalid, but it has
to be run only *after* the cache is loaded.
Refactor `CacheDistributor` a bit such that the validation (and possible
recreation) of virtualenvs happens only after the cache is loaded.
* poetry: Bump cache primary key
											
										 
											2023-01-03 18:13:00 +02:00
										 |  |  |   let saveStateSpy: jest.SpyInstance; | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  |   let getStateSpy: jest.SpyInstance; | 
					
						
							|  |  |  |   let getInputSpy: jest.SpyInstance; | 
					
						
							|  |  |  |   let setFailedSpy: jest.SpyInstance; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // cache spy
 | 
					
						
							|  |  |  |   let saveCacheSpy: jest.SpyInstance; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // exec spy
 | 
					
						
							|  |  |  |   let getExecOutputSpy: jest.SpyInstance; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let inputs = {} as any; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   beforeEach(() => { | 
					
						
							|  |  |  |     process.env['RUNNER_OS'] = process.env['RUNNER_OS'] ?? 'linux'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     infoSpy = jest.spyOn(core, 'info'); | 
					
						
							|  |  |  |     infoSpy.mockImplementation(input => undefined); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     warningSpy = jest.spyOn(core, 'warning'); | 
					
						
							|  |  |  |     warningSpy.mockImplementation(input => undefined); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     debugSpy = jest.spyOn(core, 'debug'); | 
					
						
							|  |  |  |     debugSpy.mockImplementation(input => undefined); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Use correct Poetry config when collecting Poetry projects (#447)
* Use correct Poetry config when collecting Poetry projects
When collecting Poetry projects for caching, a '**/poetry.lock' glob is
used.  However, in order to process the Poetry configuration, the
"poetry" command is run from the repo's root directory; this causes
Poetry to return an invalid configuration when there is a Poetry project
inside an inner directory.
Instead of running a single Poetry command, glob for the same pattern,
and run a Poetry command for every discovered project.
* Fix typo: saveSatetSpy -> saveStateSpy
* poetry: Support same virtualenv appearing in multiple projects
* Add nested Poetry projects test
* poetry: Set up environment for each project individually
* tests/cache-restore: Do not look for dependency files outside `data`
When the default dependency path is used for cache distributors, they
are looking for the dependency file in the project's root (including the
source code), which leads to tests taking a significant amount of time,
especially on Windows runners.  We thus hit sporadic test failures.
Change the test cases such that dependency files are always searched for
inside of `__tests__/data`, ignoring the rest of the project.
* poetry: Simplify `virtualenvs.in-project` boolean check
* README: Explain that poetry might create multiple caches
* poetry: Run `poetry env use` only after cache is loaded
The virtualenv cache might contain invalid entries, such as virtualenvs
built in previous, buggy versions of this action.  The `poetry env use`
command will recreate virtualenvs in case they are invalid, but it has
to be run only *after* the cache is loaded.
Refactor `CacheDistributor` a bit such that the validation (and possible
recreation) of virtualenvs happens only after the cache is loaded.
* poetry: Bump cache primary key
											
										 
											2023-01-03 18:13:00 +02:00
										 |  |  |     saveStateSpy = jest.spyOn(core, 'saveState'); | 
					
						
							|  |  |  |     saveStateSpy.mockImplementation(input => undefined); | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     getStateSpy = jest.spyOn(core, 'getState'); | 
					
						
							|  |  |  |     getStateSpy.mockImplementation(input => { | 
					
						
							|  |  |  |       if (input === State.CACHE_PATHS) { | 
					
						
							|  |  |  |         return JSON.stringify([__dirname]); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return requirementsHash; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setFailedSpy = jest.spyOn(core, 'setFailed'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getInputSpy = jest.spyOn(core, 'getInput'); | 
					
						
							|  |  |  |     getInputSpy.mockImplementation(input => inputs[input]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); | 
					
						
							|  |  |  |     getExecOutputSpy.mockImplementation((input: string) => { | 
					
						
							|  |  |  |       if (input.includes('pip')) { | 
					
						
							|  |  |  |         return {stdout: 'pip', stderr: '', exitCode: 0}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return {stdout: '', stderr: 'Error occured', exitCode: 2}; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     saveCacheSpy = jest.spyOn(cache, 'saveCache'); | 
					
						
							|  |  |  |     saveCacheSpy.mockImplementation(() => undefined); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('Package manager validation', () => { | 
					
						
							|  |  |  |     it('Package manager is not provided, skip caching', async () => { | 
					
						
							|  |  |  |       inputs['cache'] = ''; | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(infoSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(saveCacheSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('Validate unchanged cache is not saved', () => { | 
					
						
							|  |  |  |     it('should not save cache for pip', async () => { | 
					
						
							|  |  |  |       inputs['cache'] = 'pip'; | 
					
						
							| 
									
										
										
										
											2023-04-06 12:02:34 +02:00
										 |  |  |       inputs['python-version'] = '3.10.0'; | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(debugSpy).toHaveBeenCalledWith( | 
					
						
							|  |  |  |         `paths for caching are ${__dirname}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(getStateSpy).toHaveBeenCalledTimes(3); | 
					
						
							|  |  |  |       expect(infoSpy).toHaveBeenCalledWith( | 
					
						
							|  |  |  |         `Cache hit occurred on the primary key ${requirementsHash}, not saving cache.` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-11-24 19:58:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('should not save cache for pipenv', async () => { | 
					
						
							|  |  |  |       inputs['cache'] = 'pipenv'; | 
					
						
							| 
									
										
										
										
											2023-04-06 12:02:34 +02:00
										 |  |  |       inputs['python-version'] = '3.10.0'; | 
					
						
							| 
									
										
										
										
											2021-11-24 19:58:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(debugSpy).toHaveBeenCalledWith( | 
					
						
							|  |  |  |         `paths for caching are ${__dirname}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(getStateSpy).toHaveBeenCalledTimes(3); | 
					
						
							|  |  |  |       expect(infoSpy).toHaveBeenCalledWith( | 
					
						
							|  |  |  |         `Cache hit occurred on the primary key ${requirementsHash}, not saving cache.` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('action saves the cache', () => { | 
					
						
							|  |  |  |     it('saves cache from pip', async () => { | 
					
						
							|  |  |  |       inputs['cache'] = 'pip'; | 
					
						
							| 
									
										
										
										
											2023-04-06 12:02:34 +02:00
										 |  |  |       inputs['python-version'] = '3.10.0'; | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  |       getStateSpy.mockImplementation((name: string) => { | 
					
						
							|  |  |  |         if (name === State.CACHE_MATCHED_KEY) { | 
					
						
							|  |  |  |           return requirementsHash; | 
					
						
							|  |  |  |         } else if (name === State.CACHE_PATHS) { | 
					
						
							|  |  |  |           return JSON.stringify([__dirname]); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return pipFileLockHash; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(getStateSpy).toHaveBeenCalledTimes(3); | 
					
						
							|  |  |  |       expect(infoSpy).not.toHaveBeenCalledWith( | 
					
						
							|  |  |  |         `Cache hit occurred on the primary key ${requirementsHash}, not saving cache.` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(saveCacheSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(infoSpy).toHaveBeenLastCalledWith( | 
					
						
							|  |  |  |         `Cache saved with the key: ${pipFileLockHash}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('saves cache from pipenv', async () => { | 
					
						
							|  |  |  |       inputs['cache'] = 'pipenv'; | 
					
						
							| 
									
										
										
										
											2023-04-06 12:02:34 +02:00
										 |  |  |       inputs['python-version'] = '3.10.0'; | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  |       getStateSpy.mockImplementation((name: string) => { | 
					
						
							|  |  |  |         if (name === State.CACHE_MATCHED_KEY) { | 
					
						
							|  |  |  |           return pipFileLockHash; | 
					
						
							|  |  |  |         } else if (name === State.CACHE_PATHS) { | 
					
						
							|  |  |  |           return JSON.stringify([__dirname]); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return requirementsHash; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(getStateSpy).toHaveBeenCalledTimes(3); | 
					
						
							|  |  |  |       expect(infoSpy).not.toHaveBeenCalledWith( | 
					
						
							|  |  |  |         `Cache hit occurred on the primary key ${pipFileLockHash}, not saving cache.` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(saveCacheSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(infoSpy).toHaveBeenLastCalledWith( | 
					
						
							|  |  |  |         `Cache saved with the key: ${requirementsHash}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-11-24 19:58:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('saves cache from poetry', async () => { | 
					
						
							|  |  |  |       inputs['cache'] = 'poetry'; | 
					
						
							| 
									
										
										
										
											2023-04-06 12:02:34 +02:00
										 |  |  |       inputs['python-version'] = '3.10.0'; | 
					
						
							| 
									
										
										
										
											2021-11-24 19:58:34 +00:00
										 |  |  |       getStateSpy.mockImplementation((name: string) => { | 
					
						
							|  |  |  |         if (name === State.CACHE_MATCHED_KEY) { | 
					
						
							|  |  |  |           return poetryLockHash; | 
					
						
							|  |  |  |         } else if (name === State.CACHE_PATHS) { | 
					
						
							|  |  |  |           return JSON.stringify([__dirname]); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return requirementsHash; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(getStateSpy).toHaveBeenCalledTimes(3); | 
					
						
							|  |  |  |       expect(infoSpy).not.toHaveBeenCalledWith( | 
					
						
							|  |  |  |         `Cache hit occurred on the primary key ${poetryLockHash}, not saving cache.` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(saveCacheSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(infoSpy).toHaveBeenLastCalledWith( | 
					
						
							|  |  |  |         `Cache saved with the key: ${requirementsHash}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-06-28 15:17:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('saves with -1 cacheId , should not fail workflow', async () => { | 
					
						
							|  |  |  |       inputs['cache'] = 'poetry'; | 
					
						
							| 
									
										
										
										
											2023-04-06 12:02:34 +02:00
										 |  |  |       inputs['python-version'] = '3.10.0'; | 
					
						
							| 
									
										
										
										
											2022-06-28 15:17:50 +02:00
										 |  |  |       getStateSpy.mockImplementation((name: string) => { | 
					
						
							|  |  |  |         if (name === State.STATE_CACHE_PRIMARY_KEY) { | 
					
						
							|  |  |  |           return poetryLockHash; | 
					
						
							|  |  |  |         } else if (name === State.CACHE_PATHS) { | 
					
						
							|  |  |  |           return JSON.stringify([__dirname]); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return requirementsHash; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       saveCacheSpy.mockImplementation(() => { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(getStateSpy).toHaveBeenCalledTimes(3); | 
					
						
							|  |  |  |       expect(infoSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(saveCacheSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(infoSpy).not.toHaveBeenLastCalledWith( | 
					
						
							|  |  |  |         `Cache saved with the key: ${poetryLockHash}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-10 12:15:18 +01:00
										 |  |  |     it('saves with error from toolkit, should not fail the workflow', async () => { | 
					
						
							| 
									
										
										
										
											2022-06-28 15:17:50 +02:00
										 |  |  |       inputs['cache'] = 'npm'; | 
					
						
							| 
									
										
										
										
											2023-04-06 12:02:34 +02:00
										 |  |  |       inputs['python-version'] = '3.10.0'; | 
					
						
							| 
									
										
										
										
											2022-06-28 15:17:50 +02:00
										 |  |  |       getStateSpy.mockImplementation((name: string) => { | 
					
						
							|  |  |  |         if (name === State.STATE_CACHE_PRIMARY_KEY) { | 
					
						
							|  |  |  |           return poetryLockHash; | 
					
						
							|  |  |  |         } else if (name === State.CACHE_PATHS) { | 
					
						
							|  |  |  |           return JSON.stringify([__dirname]); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return requirementsHash; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       saveCacheSpy.mockImplementation(() => { | 
					
						
							|  |  |  |         throw new cache.ValidationError('Validation failed'); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(getInputSpy).toHaveBeenCalled(); | 
					
						
							|  |  |  |       expect(getStateSpy).toHaveBeenCalledTimes(3); | 
					
						
							|  |  |  |       expect(infoSpy).not.toHaveBeenCalledWith(); | 
					
						
							|  |  |  |       expect(saveCacheSpy).toHaveBeenCalled(); | 
					
						
							| 
									
										
										
										
											2023-03-10 12:15:18 +01:00
										 |  |  |       expect(setFailedSpy).not.toHaveBeenCalled(); | 
					
						
							| 
									
										
										
										
											2022-06-28 15:17:50 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-11-17 13:31:22 +03:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   afterEach(() => { | 
					
						
							|  |  |  |     jest.resetAllMocks(); | 
					
						
							|  |  |  |     jest.clearAllMocks(); | 
					
						
							|  |  |  |     inputs = {}; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); |