add tests for ScriptRunner and GitRunner, major moving around of logic
							parent
							
								
									a70ae91ce4
								
							
						
					
					
						commit
						928a08b842
					
				| @ -0,0 +1,22 @@ | ||||
| package alfred.web.core | ||||
| 
 | ||||
| import alfred.web.core.build.BuildId | ||||
| import org.springframework.beans.factory.annotation.Value | ||||
| import org.springframework.stereotype.Service | ||||
| import java.nio.file.Paths | ||||
| 
 | ||||
| @Service | ||||
| class AlfredHome( | ||||
|     @Value("\${ALFRED_HOME}") | ||||
|     private val home: String, | ||||
| ) { | ||||
| 
 | ||||
|     val homePath = Paths.get(home) | ||||
| 
 | ||||
|     fun logsDir() = | ||||
|         homePath.resolve("logs") | ||||
| 
 | ||||
|     fun buildConfig(buildId: BuildId) = | ||||
|         homePath.resolve("builds/${buildId}.properties") | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,64 @@ | ||||
| package alfred.web.core.process | ||||
| 
 | ||||
| import alfred.web.core.Handle | ||||
| import alfred.web.core.Handles | ||||
| import alfred.web.core.build.BuildContext | ||||
| import alfred.web.core.build.BuildId | ||||
| import alfred.web.core.build.Workspace | ||||
| import org.springframework.stereotype.Service | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| @Service | ||||
| class Git( | ||||
|     val processes: Processes, | ||||
|     val handles: Handles, | ||||
| ) { | ||||
| 
 | ||||
|     fun clone(ctx: BuildContext, ws: Workspace) { | ||||
|         ctx.log("cloning ${ctx.config.gitRepo} into $ws") | ||||
| 
 | ||||
|         val proc = processes.builder(ctx.config, ctx.logFile, "") | ||||
|             .command("git", "clone", ctx.config.gitRepo, ".") | ||||
|             .directory(ws.toFile()) | ||||
|             .start() | ||||
|         handles.add(Handle(proc.toHandle(), ctx.buildId)) | ||||
| 
 | ||||
|         val cloneSuccess = proc.waitFor(ctx.config.gitCloneTimeout, TimeUnit.SECONDS) | ||||
|         if (!cloneSuccess) { | ||||
|             throw FailedToClone(ctx.buildId, ctx.config.gitRepo ?: "[no repo configured]") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun checkout(ctx: BuildContext, ws: Workspace) { | ||||
|         ctx.log("checkout rev ${ctx.rev}") | ||||
| 
 | ||||
|         val proc = processes.builder(ctx.config, ctx.logFile, "") | ||||
|             .command("git", "checkout", ctx.rev) | ||||
|             .directory(ws.toFile()) | ||||
|             .start() | ||||
|         handles.add(Handle(proc.toHandle(), ctx.buildId)) | ||||
| 
 | ||||
|         val checkoutSuccess = proc.waitFor(ctx.config.gitCloneTimeout, TimeUnit.SECONDS) | ||||
|         if (!checkoutSuccess) { | ||||
|             throw FailedToCheckout(ctx.buildId, ctx.config.gitRepo!!, ctx.rev) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     sealed class GitException(msg: String): Exception(msg) | ||||
| 
 | ||||
|     private class FailedToClone( | ||||
|         buildId: BuildId, | ||||
|         gitRepo: String | ||||
|     ) : GitException( | ||||
|         "failed to clone $gitRepo for build id $buildId" | ||||
|     ) | ||||
| 
 | ||||
|     private class FailedToCheckout( | ||||
|         buildId: BuildId, | ||||
|         gitRepo: String, | ||||
|         rev: String | ||||
|     ) : GitException( | ||||
|         "failed to checkout revision $rev on repo $gitRepo for build id $buildId" | ||||
|     ) | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,34 @@ | ||||
| package alfred.web.core.process | ||||
| 
 | ||||
| import alfred.web.core.Handle | ||||
| import alfred.web.core.Handles | ||||
| import alfred.web.core.build.BuildContext | ||||
| import alfred.web.core.build.Workspace | ||||
| import org.springframework.stereotype.Service | ||||
| import java.io.File | ||||
| 
 | ||||
| @Service | ||||
| class Script( | ||||
|     val handles: Handles, | ||||
|     val processes: Processes | ||||
| ) { | ||||
| 
 | ||||
|     fun execute(ctx: BuildContext, ws: Workspace, scriptFile: File): Process { | ||||
|         ctx.log("Running build file: ${scriptFile.name}") | ||||
| 
 | ||||
|         val scriptProcess = processes.builder(ctx.config, ctx.logFile, ctx.rev) | ||||
|             .command(scriptFile.absolutePath) | ||||
|             .directory(ws.toFile()) | ||||
|             .start() | ||||
|         handles.add(Handle(scriptProcess.toHandle(), ctx.buildId)) | ||||
| 
 | ||||
|         return scriptProcess | ||||
|     } | ||||
| 
 | ||||
|     fun exists(scriptFile: File) = scriptFile.exists() | ||||
| 
 | ||||
|     fun onExit(process: Process, block: () -> Unit) { | ||||
|         process.onExit().whenComplete { _, _ -> block() } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| package alfred.web.core | ||||
| 
 | ||||
| import io.mockk.justRun | ||||
| import io.mockk.mockk | ||||
| import org.springframework.context.ApplicationEventPublisher | ||||
| import kotlin.reflect.KClass | ||||
| 
 | ||||
| fun <T : Any> eventPublisher(eventType: KClass<T>): ApplicationEventPublisher { | ||||
|     val eventPublisher = mockk<ApplicationEventPublisher>() | ||||
|     justRun { eventPublisher.publishEvent(any(eventType)) } | ||||
|     return eventPublisher | ||||
| } | ||||
| @ -0,0 +1,73 @@ | ||||
| package alfred.web.core.runner | ||||
| 
 | ||||
| import alfred.web.core.AlfredHome | ||||
| import alfred.web.core.build.Builds | ||||
| import alfred.web.core.build.Workspaces | ||||
| import alfred.web.core.event.BuildFinished | ||||
| import alfred.web.core.eventPublisher | ||||
| import alfred.web.core.process.Git | ||||
| import alfred.web.core.process.ProcessEnvironment | ||||
| import alfred.web.core.process.Processes | ||||
| import alfred.web.core.process.Script | ||||
| import io.mockk.every | ||||
| import io.mockk.justRun | ||||
| import io.mockk.mockk | ||||
| import io.mockk.spyk | ||||
| import io.mockk.verify | ||||
| import org.junit.jupiter.api.Test | ||||
| import org.junit.jupiter.api.io.TempDir | ||||
| import java.nio.file.Path | ||||
| import java.nio.file.Paths | ||||
| 
 | ||||
| class GitRunnerTest { | ||||
| 
 | ||||
|     @Test | ||||
|     fun runs(@TempDir logsDir: Path) { | ||||
|         // Given | ||||
|         val eventPublisher = eventPublisher(BuildFinished::class) | ||||
| 
 | ||||
|         val git = mockk<Git>() | ||||
|         justRun { git.clone(any(), any()) } | ||||
|         justRun { git.checkout(any(), any()) } | ||||
| 
 | ||||
|         val script = mockk<Script>() | ||||
|         every { script.execute(any(), any(), any()) } returns (mockk<Process>(relaxed = true)) | ||||
|         every { script.exists(any()) } returns (true) | ||||
| 
 | ||||
|         val workspacesSpy = spyk(Workspaces()) | ||||
| 
 | ||||
|         val homeDir = Paths.get("").toAbsolutePath().resolve("src/test/resources/home-1") | ||||
|         val home = spyk(AlfredHome(homeDir.toFile().absolutePath)) | ||||
|         every { home.logsDir() } returns logsDir | ||||
| 
 | ||||
|         val runner = GitRunner( | ||||
|             builds = Builds(home), | ||||
|             processes = Processes(ProcessEnvironment()), | ||||
|             workspaces = workspacesSpy, | ||||
|             eventPublisher = eventPublisher, | ||||
|             git = git, | ||||
|             script = script | ||||
|         ) | ||||
| 
 | ||||
|         // When | ||||
|         runner.run( | ||||
|             buildId = "simple-git", | ||||
|             rev = "master" | ||||
|         ) | ||||
| 
 | ||||
|         // Then | ||||
|         verify(timeout = 1_000, exactly = 1) { | ||||
|             git.clone(any(), any()) | ||||
|             git.checkout(any(), any()) | ||||
| 
 | ||||
|             listOf("pre.sh", "job.sh", "post.sh").forEach { scriptName -> | ||||
|                 script.exists(match { it.name == scriptName }) | ||||
|                 script.execute(any(), any(), match { it.name == scriptName }) | ||||
|             } | ||||
| 
 | ||||
|             eventPublisher.publishEvent(any(BuildFinished::class)) | ||||
|             workspacesSpy.cleanUp(any()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,65 @@ | ||||
| package alfred.web.core.runner | ||||
| 
 | ||||
| import alfred.web.core.AlfredHome | ||||
| import alfred.web.core.build.Builds | ||||
| import alfred.web.core.build.Workspaces | ||||
| import alfred.web.core.event.BuildFinished | ||||
| import alfred.web.core.eventPublisher | ||||
| import alfred.web.core.process.Script | ||||
| import io.mockk.every | ||||
| import io.mockk.mockk | ||||
| import io.mockk.slot | ||||
| import io.mockk.spyk | ||||
| import io.mockk.verify | ||||
| import org.junit.jupiter.api.Test | ||||
| import org.junit.jupiter.api.io.TempDir | ||||
| import org.springframework.test.web.client.ExpectedCount.once | ||||
| import java.nio.file.Path | ||||
| import java.nio.file.Paths | ||||
| 
 | ||||
| 
 | ||||
| class ScriptRunnerTest { | ||||
| 
 | ||||
|     @Test | ||||
|     fun runs(@TempDir logDir: Path) { | ||||
|         // Given | ||||
|         val home = spyk( | ||||
|             AlfredHome( | ||||
|                 Paths.get("").toAbsolutePath().resolve("src/test/resources/home-1").toString() | ||||
|             ) | ||||
|         ) | ||||
|         every { home.logsDir() } returns logDir | ||||
| 
 | ||||
|         val eventPublisher = eventPublisher(BuildFinished::class) | ||||
| 
 | ||||
|         val script = mockk<Script>() | ||||
|         every { script.execute(any(), any(), any()) } returns mockk<Process>(relaxed = true) | ||||
|         val onExit = slot<() -> Unit>() | ||||
|         every { script.onExit(any(), capture(onExit)) } answers { onExit.captured() } | ||||
| 
 | ||||
|         val workspaces = spyk(Workspaces()) | ||||
| 
 | ||||
|         val runner = ScriptRunner( | ||||
|             builds = Builds(home), | ||||
|             workspaces = workspaces, | ||||
|             eventPublisher = eventPublisher, | ||||
|             script = script | ||||
|         ) | ||||
| 
 | ||||
|         // When | ||||
|         runner.run( | ||||
|             buildId = "simple-script", | ||||
|             rev = null | ||||
|         ) | ||||
| 
 | ||||
|         // Then | ||||
|         verify(timeout = 1_000, exactly = 1) { | ||||
|             script.execute(any(), any(), match { it.name == "some-script" }) | ||||
| 
 | ||||
|             workspaces.cleanUp(any()) | ||||
| 
 | ||||
|             eventPublisher.publishEvent(any(BuildFinished::class)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,5 @@ | ||||
| user=alfred | ||||
| workspace=/tmp | ||||
| apikey=Mellon | ||||
| 
 | ||||
| git.repo.url=some.url | ||||
| @ -0,0 +1,5 @@ | ||||
| user=alfred | ||||
| workspace=/tmp | ||||
| apikey=Mellon | ||||
| 
 | ||||
| script=some-script | ||||
					Loading…
					
					
				
		Reference in New Issue
	
	 Josha von Gizycki
						Josha von Gizycki