Initial commit

This commit is contained in:
Peter Niederwieser
2016-01-19 14:51:19 +01:00
committed by Dan Chao
commit ecad035dca
2972 changed files with 211653 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
amends "pkl:Project"
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
}

View File

@@ -0,0 +1,229 @@
/**
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.cli
import java.nio.file.Path
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer
import org.pkl.core.packages.PackageUri
class CliDownloadPackageCommandTest {
companion object {
@BeforeAll
@JvmStatic
fun beforeAll() {
PackageServer.ensureStarted()
}
}
@Test
fun `download packages`(@TempDir tempDir: Path) {
val cmd =
CliDownloadPackageCommand(
baseOptions =
CliBaseOptions(
moduleCacheDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
packageUris =
listOf(
PackageUri("package://localhost:12110/birds@0.5.0"),
PackageUri("package://localhost:12110/fruit@1.0.5"),
PackageUri("package://localhost:12110/fruit@1.1.0")
),
noTranstive = true
)
cmd.run()
assertThat(tempDir.resolve("package-1/localhost:12110/birds@0.5.0/birds@0.5.0.zip")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/birds@0.5.0/birds@0.5.0.json")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/fruit@1.0.5/fruit@1.0.5.zip")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/fruit@1.0.5/fruit@1.0.5.json")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/fruit@1.1.0/fruit@1.1.0.zip")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/fruit@1.1.0/fruit@1.1.0.json")).exists()
}
@Test
fun `download packages with cache dir set by project`(@TempDir tempDir: Path) {
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
evaluatorSettings {
moduleCacheDir = ".my-cache"
}
"""
.trimIndent()
)
val cmd =
CliDownloadPackageCommand(
baseOptions =
CliBaseOptions(
workingDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
packageUris = listOf(PackageUri("package://localhost:12110/birds@0.5.0")),
noTranstive = true
)
cmd.run()
assertThat(tempDir.resolve(".my-cache/package-1/localhost:12110/birds@0.5.0/birds@0.5.0.zip"))
.exists()
assertThat(tempDir.resolve(".my-cache/package-1/localhost:12110/birds@0.5.0/birds@0.5.0.json"))
.exists()
}
@Test
fun `download package while specifying checksum`(@TempDir tempDir: Path) {
val cmd =
CliDownloadPackageCommand(
baseOptions =
CliBaseOptions(
moduleCacheDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
packageUris =
listOf(
PackageUri(
"package://localhost:12110/birds@0.5.0::sha256:3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
),
),
noTranstive = true
)
cmd.run()
assertThat(tempDir.resolve("package-1/localhost:12110/birds@0.5.0/birds@0.5.0.zip")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/birds@0.5.0/birds@0.5.0.json")).exists()
}
@Test
fun `download package with invalid checksum`(@TempDir tempDir: Path) {
val cmd =
CliDownloadPackageCommand(
baseOptions =
CliBaseOptions(
moduleCacheDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
packageUris =
listOf(
PackageUri("package://localhost:12110/birds@0.5.0::sha256:intentionallyBogusChecksum"),
),
noTranstive = true
)
assertThatCode { cmd.run() }
.hasMessage(
"""
Cannot download package `package://localhost:12110/birds@0.5.0` because the computed checksum for package metadata does not match the expected checksum.
Computed checksum: "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
Expected checksum: "intentionallyBogusChecksum"
Asset URL: "https://localhost:12110/birds@0.5.0"
"""
.trimIndent()
)
}
@Test
fun `disabling cacheing is an error`(@TempDir tempDir: Path) {
val cmd =
CliDownloadPackageCommand(
baseOptions = CliBaseOptions(workingDir = tempDir, noCache = true),
packageUris = listOf(PackageUri("package://localhost:12110/birds@0.5.0")),
noTranstive = true
)
assertThatCode { cmd.run() }
.hasMessage("Cannot download packages because no cache directory is specified.")
}
@Test
fun `download packages with bad checksum`(@TempDir tempDir: Path) {
val cmd =
CliDownloadPackageCommand(
baseOptions =
CliBaseOptions(
moduleCacheDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
packageUris = listOf(PackageUri("package://localhost:12110/badChecksum@1.0.0")),
noTranstive = true
)
assertThatCode { cmd.run() }
.hasMessageStartingWith(
"Cannot download package `package://localhost:12110/badChecksum@1.0.0` because the computed checksum does not match the expected checksum."
)
}
@Test
fun `download multiple failing packages`(@TempDir tempDir: Path) {
val cmd =
CliDownloadPackageCommand(
baseOptions =
CliBaseOptions(
moduleCacheDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
packageUris =
listOf(
PackageUri("package://localhost:12110/badChecksum@1.0.0"),
PackageUri("package://bogus.domain/notAPackage@1.0.0")
),
noTranstive = true
)
assertThatCode { cmd.run() }
.hasMessage(
"""
Failed to download some packages.
Failed to download package://localhost:12110/badChecksum@1.0.0 because:
Cannot download package `package://localhost:12110/badChecksum@1.0.0` because the computed checksum does not match the expected checksum.
Computed checksum: "0ec8a501e974802d0b71b8d58141e1e6eaa10bc2033e18200be3a978823d98aa"
Expected checksum: "intentionally bogus checksum"
Asset URL: "https://localhost:12110/badChecksum@1.0.0/badChecksum@1.0.0.zip"
Failed to download package://bogus.domain/notAPackage@1.0.0 because:
Exception when making request `GET https://bogus.domain/notAPackage@1.0.0`:
bogus.domain
"""
.trimIndent()
)
}
@Test
fun `download package, including transitive dependencies`(@TempDir tempDir: Path) {
CliDownloadPackageCommand(
baseOptions =
CliBaseOptions(
moduleCacheDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
packageUris = listOf(PackageUri("package://localhost:12110/birds@0.5.0")),
noTranstive = false
)
.run()
assertThat(tempDir.resolve("package-1/localhost:12110/birds@0.5.0/birds@0.5.0.zip")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/birds@0.5.0/birds@0.5.0.json")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/fruit@1.0.5/fruit@1.0.5.zip")).exists()
assertThat(tempDir.resolve("package-1/localhost:12110/fruit@1.0.5/fruit@1.0.5.json")).exists()
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
/**
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.cli
import com.github.ajalt.clikt.core.BadParameterValue
import com.github.ajalt.clikt.core.subcommands
import java.nio.file.Path
import kotlin.io.path.createDirectory
import kotlin.io.path.createSymbolicLinkPointingTo
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.AssertionsForClassTypes.assertThatCode
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.cli.commands.EvalCommand
import org.pkl.cli.commands.RootCommand
import org.pkl.commons.writeString
class CliMainTest {
private val evalCmd = EvalCommand("")
private val cmd = RootCommand("pkl", "pkl version 1", "").subcommands(evalCmd)
@Test
fun `duplicate CLI option produces meaningful errror message`(@TempDir tempDir: Path) {
val inputFile = tempDir.resolve("test.pkl").writeString("").toString()
assertThatCode {
cmd.parse(arrayOf("eval", "--output-path", "path1", "--output-path", "path2", inputFile))
}
.hasMessage("Invalid value for \"--output-path\": Option cannot be repeated")
assertThatCode {
cmd.parse(arrayOf("eval", "-o", "path1", "--output-path", "path2", inputFile))
}
.hasMessage("Invalid value for \"--output-path\": Option cannot be repeated")
}
@Test
fun `eval requires at least one file`() {
assertThatCode { cmd.parse(arrayOf("eval")) }.hasMessage("""Missing argument "<modules>"""")
}
@Test
fun `output to symlinked directory works`(@TempDir tempDir: Path) {
val code =
"""
x = 3
output {
value = x
renderer = new JsonRenderer {}
}
"""
.trimIndent()
val inputFile = tempDir.resolve("test.pkl").writeString(code).toString()
val outputFile = makeSymdir(tempDir, "out", "linkOut").resolve("test.pkl").toString()
assertThatCode { cmd.parse(arrayOf("eval", inputFile, "-o", outputFile)) }
.doesNotThrowAnyException()
}
@Test
fun `cannot have multiple output with -o or -x`(@TempDir tempDir: Path) {
val testIn = makeInput(tempDir)
val testOut = tempDir.resolve("test").toString()
val error =
"""Invalid value for "--multiple-file-output-path": Option is mutually exclusive with -o, --output-path and -x, --expression."""
assertThatCode { cmd.parse(arrayOf("eval", "-m", testOut, "-x", "x", testIn)) }
.hasMessage(error)
assertThatCode { cmd.parse(arrayOf("eval", "-m", testOut, "-o", "/tmp/test", testIn)) }
.hasMessage(error)
}
@Test
fun `showing version works`() {
assertThatCode { cmd.parse(arrayOf("--version")) }.hasMessage("pkl version 1")
}
@Test
fun `file paths get parsed into URIs`(@TempDir tempDir: Path) {
cmd.parse(arrayOf("eval", makeInput(tempDir, "my file.txt")))
val modules = evalCmd.baseOptions.baseOptions(evalCmd.modules).normalizedSourceModules
assertThat(modules).hasSize(1)
assertThat(modules[0].path).endsWith("my file.txt")
}
@Test
fun `invalid URIs are not accepted`() {
val ex = assertThrows<BadParameterValue> { cmd.parse(arrayOf("eval", "file:my file.txt")) }
assertThat(ex.message).contains("URI `file:my file.txt` has invalid syntax")
}
private fun makeInput(tempDir: Path, fileName: String = "test.pkl"): String {
val code = "x = 1"
return tempDir.resolve(fileName).writeString(code).toString()
}
private fun makeSymdir(baseDir: Path, name: String, linkName: String): Path {
val dir = baseDir.resolve(name)
val link = baseDir.resolve(linkName)
dir.createDirectory()
link.createSymbolicLinkPointingTo(dir)
return link
}
}

View File

@@ -0,0 +1,959 @@
/**
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.cli
import java.io.StringWriter
import java.net.URI
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.util.stream.Collectors
import kotlin.io.path.createDirectories
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
import org.pkl.commons.cli.CliTestOptions
import org.pkl.commons.readString
import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer
import org.pkl.commons.writeString
import org.pkl.core.runtime.CertificateUtils
class CliProjectPackagerTest {
@Test
fun `missing PklProject when inferring a project dir`(@TempDir tempDir: Path) {
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err).hasMessageStartingWith("No project visible to the working directory.")
}
@Test
fun `missing PklProject when explict dir is provided`(@TempDir tempDir: Path) {
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err).hasMessageStartingWith("Directory $tempDir does not contain a PklProject file.")
}
@Test
fun `PklProject missing package section`(@TempDir tempDir: Path) {
tempDir
.resolve("PklProject")
.writeString(
"""
amends "pkl:Project"
"""
.trimIndent()
)
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err)
.hasMessageStartingWith("No package was declared in project `${tempDir.toUri()}PklProject`.")
}
@Test
fun `failing apiTests`(@TempDir tempDir: Path) {
tempDir.writeFile(
"myTest.pkl",
"""
amends "pkl:test"
facts {
["1 == 2"] {
1 == 2
}
}
"""
.trimIndent()
)
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
apiTests { "myTest.pkl" }
}
"""
.trimIndent()
)
val buffer = StringWriter()
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = buffer
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err).hasMessageContaining("because its API tests are failing")
assertThat(buffer.toString()).contains("1 == 2")
}
@Test
fun `passing apiTests`(@TempDir tempDir: Path) {
tempDir
.resolve("myTest.pkl")
.writeString(
"""
amends "pkl:test"
facts {
["1 == 1"] {
1 == 1
}
}
"""
.trimIndent()
)
tempDir
.resolve("PklProject")
.writeString(
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
apiTests { "myTest.pkl" }
}
"""
.trimIndent()
)
val buffer = StringWriter()
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = buffer
)
packager.run()
}
@Test
fun `apiTests that import dependencies`(@TempDir tempDir: Path) {
val cacheDir = tempDir.resolve("cache")
val projectDir = tempDir.resolve("myProject").createDirectories()
PackageServer.populateCacheDir(cacheDir)
projectDir
.resolve("myTest.pkl")
.writeString(
"""
amends "pkl:test"
import "@birds/Bird.pkl"
examples {
["Bird"] {
new Bird { name = "Finch"; favoriteFruit { name = "Tangerine" } }
}
}
"""
.trimIndent()
)
projectDir
.resolve("PklProject")
.writeString(
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
apiTests { "myTest.pkl" }
}
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
}
"""
.trimIndent()
)
projectDir
.resolve("PklProject.deps.json")
.writeString(
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/fruit@1": {
"type": "remote",
"uri": "projectpackage://localhost:12110/fruit@1.0.5",
"checksums": {
"sha256": "b4ea243de781feeab7921227591e6584db5d0673340f30fab2ffe8ad5c9f75f5"
}
}
}
}
"""
.trimIndent()
)
val buffer = StringWriter()
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = projectDir, moduleCacheDir = cacheDir),
listOf(projectDir),
CliTestOptions(),
".out",
skipPublishCheck = true,
consoleWriter = buffer
)
packager.run()
}
@Test
fun `generate package`(@TempDir tempDir: Path) {
val fooPkl =
tempDir.writeFile(
"a/b/foo.pkl",
"""
module foo
name: String
"""
.trimIndent()
)
val fooTxt =
tempDir.writeFile(
"c/d/foo.txt",
"""
foo
bar
baz
"""
.trimIndent()
)
tempDir
.resolve("PklProject")
.writeString(
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val packager =
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
packager.run()
val expectedMetadata = tempDir.resolve(".out/mypackage@1.0.0/mypackage@1.0.0")
val expectedMetadataChecksum = tempDir.resolve(".out/mypackage@1.0.0/mypackage@1.0.0.sha256")
val expectedArchive = tempDir.resolve(".out/mypackage@1.0.0/mypackage@1.0.0.zip")
val expectedArchiveChecksum = tempDir.resolve(".out/mypackage@1.0.0/mypackage@1.0.0.zip.sha256")
assertThat(expectedMetadata).exists()
assertThat(expectedMetadata)
.hasContent(
"""
{
"name": "mypackage",
"packageUri": "package://example.com/mypackage@1.0.0",
"version": "1.0.0",
"packageZipUrl": "https://foo.com",
"packageZipChecksums": {
"sha256": "7f515fbc4b229ba171fac78c7c3f2c2e68e2afebae8cfba042b12733943a2813"
},
"dependencies": {},
"authors": []
}
"""
.trimIndent()
)
assertThat(expectedArchive).exists()
assertThat(expectedArchive.zipFilePaths())
.hasSameElementsAs(listOf("/", "/c", "/c/d", "/c/d/foo.txt", "/a", "/a/b", "/a/b/foo.pkl"))
assertThat(expectedMetadataChecksum)
.hasContent("203ef387f217a3caee7f19819ef2b50926929269241cb7b3a29d95237b2c7f4b")
assertThat(expectedArchiveChecksum)
.hasContent("7f515fbc4b229ba171fac78c7c3f2c2e68e2afebae8cfba042b12733943a2813")
FileSystems.newFileSystem(URI("jar:" + expectedArchive.toUri()), mutableMapOf<String, String>())
.use { fs ->
assertThat(fs.getPath("a/b/foo.pkl")).hasSameTextualContentAs(fooPkl)
assertThat(fs.getPath("c/d/foo.txt")).hasSameTextualContentAs(fooTxt)
}
}
@Test
fun `generate package with excludes`(@TempDir tempDir: Path) {
tempDir.apply {
writeEmptyFile("a/b/c/d.bin")
writeEmptyFile("input/foo/bar.txt")
writeEmptyFile("z.bin")
writeEmptyFile("main.pkl")
writeEmptyFile("main.test.pkl")
writeEmptyFile("child/main.pkl")
writeEmptyFile("child/main.test.pkl")
}
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
exclude {
"*.bin"
"child/main.pkl"
"*.test.pkl"
}
}
"""
.trimIndent()
)
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
.run()
val expectedArchive = tempDir.resolve(".out/mypackage@1.0.0/mypackage@1.0.0.zip")
assertThat(expectedArchive.zipFilePaths())
.hasSameElementsAs(
listOf(
"/",
"/a",
"/a/b",
"/a/b/c",
"/child",
"/input",
"/input/foo",
"/input/foo/bar.txt",
"/main.pkl",
)
)
}
@Test
fun `generate packages with local dependencies`(@TempDir tempDir: Path) {
val projectDir = tempDir.resolve("project")
val project2Dir = tempDir.resolve("project2")
projectDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
["project2"] = import("../project2/PklProject")
}
"""
.trimIndent()
)
projectDir.writeFile(
"PklProject.deps.json",
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/project2@5": {
"type": "local",
"uri": "projectpackage://localhost:12110/project2@5.0.0",
"path": "../project2"
}
}
}
"""
.trimIndent()
)
project2Dir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "project2"
baseUri = "package://localhost:12110/project2"
version = "5.0.0"
packageZipUrl = "https://foo.com/project2.zip"
}
"""
.trimIndent()
)
project2Dir.writeFile(
"PklProject.deps.json",
"""
{
"schemaVersion": 1,
"resolvedDependencies": {}
}
"""
.trimIndent()
)
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(projectDir, project2Dir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
.run()
val expectedMetadata = tempDir.resolve(".out/mypackage@1.0.0/mypackage@1.0.0")
assertThat(expectedMetadata).exists()
assertThat(expectedMetadata)
.hasContent(
"""
{
"name": "mypackage",
"packageUri": "package://example.com/mypackage@1.0.0",
"version": "1.0.0",
"packageZipUrl": "https://foo.com",
"packageZipChecksums": {
"sha256": "7d08a65078e0bfc382c16fe1bb94546ab9a11e6f551087f362a4515ca98102fc"
},
"dependencies": {
"birds": {
"uri": "package://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"project2": {
"uri": "package://localhost:12110/project2@5.0.0",
"checksums": {
"sha256": "ddebb806e5b218ebb1a2baa14ad206b46e7a0c1585fa9863a486c75592bc8b16"
}
}
},
"authors": []
}
"""
.trimIndent()
)
val project2Metadata = tempDir.resolve(".out/project2@5.0.0/project2@5.0.0")
assertThat(project2Metadata).exists()
assertThat(project2Metadata.readString())
.isEqualTo(
"""
{
"name": "project2",
"packageUri": "package://localhost:12110/project2@5.0.0",
"version": "5.0.0",
"packageZipUrl": "https://foo.com/project2.zip",
"packageZipChecksums": {
"sha256": "7d08a65078e0bfc382c16fe1bb94546ab9a11e6f551087f362a4515ca98102fc"
},
"dependencies": {},
"authors": []
}
"""
.trimIndent()
)
}
@Test
fun `generate package with local dependencies fails if local dep is not included for packaging`(
@TempDir tempDir: Path
) {
val projectDir = tempDir.resolve("project")
val project2Dir = tempDir.resolve("project2")
projectDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
["project2"] = import("../project2/PklProject")
}
"""
.trimIndent()
)
projectDir.writeFile(
"PklProject.deps.json",
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/project2@5": {
"type": "local",
"uri": "projectpackage://localhost:12110/project2@5.0.0",
"path": "../project2"
}
}
}
"""
.trimIndent()
)
project2Dir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "project2"
baseUri = "package://localhost:12110/project2"
version = "5.0.0"
packageZipUrl = "https://foo.com/project2.zip"
}
"""
.trimIndent()
)
project2Dir.writeFile(
"PklProject.deps.json",
"""
{
"schemaVersion": 1,
"resolvedDependencies": {}
}
"""
.trimIndent()
)
assertThatCode {
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(projectDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
.run()
}
.hasMessageContaining("which is not included for packaging")
}
@Test
fun `import path verification -- relative path outside project dir`(@TempDir tempDir: Path) {
tempDir.writeFile(
"main.pkl",
"""
import "../foo.pkl"
res = foo
"""
.trimIndent()
)
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val e =
assertThrows<CliException> {
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
.run()
}
assertThat(e.message)
.startsWith(
"""
Pkl Error
Path `../foo.pkl` includes path segments that are outside the project root directory.
1 | import "../foo.pkl"
^^^^^^^^^^^^
"""
.trimIndent()
)
}
@Test
fun `import path verification -- absolute import from root dir`(@TempDir tempDir: Path) {
tempDir.writeFile(
"main.pkl",
"""
import "$tempDir/foo.pkl"
res = foo
"""
.trimIndent()
)
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val e =
assertThrows<CliException> {
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
.run()
}
assertThat(e.message)
.startsWith(
"""
Pkl Error
Path `$tempDir/foo.pkl` includes path segments that are outside the project root directory.
"""
.trimIndent()
)
}
@Test
fun `import path verification -- absolute read from root dir`(@TempDir tempDir: Path) {
tempDir.writeFile(
"main.pkl",
"""
res = read("$tempDir/foo.pkl")
"""
.trimIndent()
)
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val e =
assertThrows<CliException> {
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
.run()
}
assertThat(e.message)
.startsWith(
"""
Pkl Error
Path `$tempDir/foo.pkl` includes path segments that are outside the project root directory.
"""
.trimIndent()
)
}
@Test
fun `import path verification -- passing`(@TempDir tempDir: Path) {
tempDir.writeFile(
"foo/bar.pkl",
"""
import "baz.pkl"
"""
.trimIndent()
)
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
package {
name = "mypackage"
version = "1.0.0"
baseUri = "package://example.com/mypackage"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = StringWriter()
)
.run()
}
@Test
fun `multiple projects`(@TempDir tempDir: Path) {
tempDir.writeFile("project1/main.pkl", "res = 1")
tempDir.writeFile(
"project1/PklProject",
"""
amends "pkl:Project"
package {
name = "project1"
version = "1.0.0"
baseUri = "package://example.com/project1"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
tempDir.writeFile("project2/main2.pkl", "res = 2")
tempDir.writeFile(
"project2/PklProject",
"""
amends "pkl:Project"
package {
name = "project2"
version = "2.0.0"
baseUri = "package://example.com/project2"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val out = StringWriter()
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir.resolve("project1"), tempDir.resolve("project2")),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = true,
consoleWriter = out
)
.run()
assertThat(out.toString())
.isEqualTo(
"""
.out/project1@1.0.0/project1@1.0.0.zip
.out/project1@1.0.0/project1@1.0.0.zip.sha256
.out/project1@1.0.0/project1@1.0.0
.out/project1@1.0.0/project1@1.0.0.sha256
.out/project2@2.0.0/project2@2.0.0.zip
.out/project2@2.0.0/project2@2.0.0.zip.sha256
.out/project2@2.0.0/project2@2.0.0
.out/project2@2.0.0/project2@2.0.0.sha256
"""
.trimIndent()
)
assertThat(tempDir.resolve(".out/project1@1.0.0/project1@1.0.0.zip").zipFilePaths())
.hasSameElementsAs(listOf("/", "/main.pkl"))
assertThat(tempDir.resolve(".out/project2@2.0.0/project2@2.0.0.zip").zipFilePaths())
.hasSameElementsAs(listOf("/", "/main2.pkl"))
}
@Test
fun `publish checks`(@TempDir tempDir: Path) {
PackageServer.ensureStarted()
CertificateUtils.setupAllX509CertificatesGlobally(listOf(FileTestUtils.selfSignedCertificate))
tempDir.writeFile("project/main.pkl", "res = 1")
tempDir.writeFile(
"project/PklProject",
// intentionally conflict with localhost:12110/birds@0.5.0 from our test fixtures
"""
amends "pkl:Project"
package {
name = "birds"
version = "0.5.0"
baseUri = "package://localhost:12110/birds"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val e =
assertThrows<CliException> {
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir.resolve("project")),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = false,
consoleWriter = StringWriter()
)
.run()
}
assertThat(e)
.hasMessageStartingWith(
"""
Package `package://localhost:12110/birds@0.5.0` was already published with different contents.
Computed checksum: 7324e17214b6dcda63ebfb57d5a29b077af785c13bed0dc22b5138628a3f8d8f
Published checksum: 3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118
"""
.trimIndent()
)
}
@Test
fun `publish check when package is not yet published`(@TempDir tempDir: Path) {
PackageServer.ensureStarted()
CertificateUtils.setupAllX509CertificatesGlobally(listOf(FileTestUtils.selfSignedCertificate))
tempDir.writeFile("project/main.pkl", "res = 1")
tempDir.writeFile(
"project/PklProject",
"""
amends "pkl:Project"
package {
name = "mangos"
version = "1.0.0"
baseUri = "package://localhost:12110/mangos"
packageZipUrl = "https://foo.com"
}
"""
.trimIndent()
)
val out = StringWriter()
CliProjectPackager(
CliBaseOptions(workingDir = tempDir),
listOf(tempDir.resolve("project")),
CliTestOptions(),
".out/%{name}@%{version}",
skipPublishCheck = false,
consoleWriter = out
)
.run()
assertThat(out.toString())
.isEqualTo(
"""
.out/mangos@1.0.0/mangos@1.0.0.zip
.out/mangos@1.0.0/mangos@1.0.0.zip.sha256
.out/mangos@1.0.0/mangos@1.0.0
.out/mangos@1.0.0/mangos@1.0.0.sha256
"""
.trimIndent()
)
}
private fun Path.zipFilePaths(): List<String> {
return FileSystems.newFileSystem(URI("jar:${toUri()}"), emptyMap<String, String>()).use { fs ->
Files.walk(fs.getPath("/")).map { it.toString() }.collect(Collectors.toList())
}
}
}

View File

@@ -0,0 +1,439 @@
/**
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.cli
import java.io.StringWriter
import java.nio.file.Path
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
import org.pkl.commons.test.FileTestUtils
import org.pkl.commons.test.PackageServer
class CliProjectResolverTest {
companion object {
@BeforeAll
@JvmStatic
fun beforeAll() {
PackageServer.ensureStarted()
}
}
@Test
fun `missing PklProject when inferring a project dir`(@TempDir tempDir: Path) {
val packager =
CliProjectResolver(
CliBaseOptions(workingDir = tempDir),
emptyList(),
consoleWriter = StringWriter(),
errWriter = StringWriter()
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err).hasMessageStartingWith("No project visible to the working directory.")
}
@Test
fun `missing PklProject when explict dir is provided`(@TempDir tempDir: Path) {
val packager =
CliProjectResolver(
CliBaseOptions(),
listOf(tempDir),
consoleWriter = StringWriter(),
errWriter = StringWriter()
)
val err = assertThrows<CliException> { packager.run() }
assertThat(err).hasMessageStartingWith("Directory $tempDir does not contain a PklProject file.")
}
@Test
fun `basic project`(@TempDir tempDir: Path) {
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
}
"""
.trimIndent()
)
CliProjectResolver(
CliBaseOptions(
workingDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
listOf(tempDir),
consoleWriter = StringWriter(),
errWriter = StringWriter()
)
.run()
val expectedOutput = tempDir.resolve("PklProject.deps.json")
assertThat(expectedOutput)
.hasContent(
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/fruit@1": {
"type": "remote",
"uri": "projectpackage://localhost:12110/fruit@1.0.5",
"checksums": {
"sha256": "b4ea243de781feeab7921227591e6584db5d0673340f30fab2ffe8ad5c9f75f5"
}
}
}
}
"""
.trimIndent()
)
}
@Test
fun `basic project, inferred from working dir`(@TempDir tempDir: Path) {
tempDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
}
"""
.trimIndent()
)
CliProjectResolver(
CliBaseOptions(
workingDir = tempDir,
caCertificates = listOf(FileTestUtils.selfSignedCertificate)
),
emptyList(),
consoleWriter = StringWriter(),
errWriter = StringWriter()
)
.run()
val expectedOutput = tempDir.resolve("PklProject.deps.json")
assertThat(expectedOutput)
.hasContent(
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/fruit@1": {
"type": "remote",
"uri": "projectpackage://localhost:12110/fruit@1.0.5",
"checksums": {
"sha256": "b4ea243de781feeab7921227591e6584db5d0673340f30fab2ffe8ad5c9f75f5"
}
}
}
}
"""
.trimIndent()
)
}
@Test
fun `local dependencies`(@TempDir tempDir: Path) {
val projectDir = tempDir.resolve("theproject")
projectDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
["project2"] = import("../project2/PklProject")
}
"""
.trimIndent()
)
projectDir.writeFile(
"../project2/PklProject",
"""
amends "pkl:Project"
package {
name = "project2"
baseUri = "package://localhost:12110/package2"
version = "5.0.0"
packageZipUrl = "https://foo.com/package2.zip"
}
dependencies {
["fruit"] {
uri = "package://localhost:12110/fruit@1.0.5"
}
["project3"] = import("../project3/PklProject")
}
"""
.trimIndent()
)
projectDir.writeFile(
"../project3/PklProject",
"""
amends "pkl:Project"
package {
name = "project3"
baseUri = "package://localhost:12110/package3"
version = "5.0.0"
packageZipUrl = "https://foo.com/package3.zip"
}
dependencies {
["fruit"] {
uri = "package://localhost:12110/fruit@1.1.0"
}
}
"""
.trimIndent()
)
CliProjectResolver(
CliBaseOptions(caCertificates = listOf(FileTestUtils.selfSignedCertificate)),
listOf(projectDir),
consoleWriter = StringWriter(),
errWriter = StringWriter()
)
.run()
val expectedOutput = projectDir.resolve("PklProject.deps.json")
assertThat(expectedOutput)
.hasContent(
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/fruit@1": {
"type": "remote",
"uri": "projectpackage://localhost:12110/fruit@1.1.0",
"checksums": {
"sha256": "98ad9fc407a79dc3fd5595e7a29c3803ade0a6957c18ec94b8a1624360b24f01"
}
},
"package://localhost:12110/package2@5": {
"type": "local",
"uri": "projectpackage://localhost:12110/package2@5.0.0",
"path": "../project2"
},
"package://localhost:12110/package3@5": {
"type": "local",
"uri": "projectpackage://localhost:12110/package3@5.0.0",
"path": "../project3"
}
}
}
"""
.trimIndent()
)
}
@Test
fun `local dependency overridden by remote dependency`(@TempDir tempDir: Path) {
val projectDir = tempDir.resolve("theproject")
projectDir.writeFile(
"PklProject",
"""
amends "pkl:Project"
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
["fruit"] = import("../fruit/PklProject")
}
"""
.trimIndent()
)
projectDir.writeFile(
"../fruit/PklProject",
"""
amends "pkl:Project"
package {
name = "fruit"
baseUri = "package://localhost:12110/fruit"
version = "1.0.0"
packageZipUrl = "https://foo.com/fruit.zip"
}
"""
.trimIndent()
)
val consoleOut = StringWriter()
val errOut = StringWriter()
CliProjectResolver(
CliBaseOptions(caCertificates = listOf(FileTestUtils.selfSignedCertificate)),
listOf(projectDir),
consoleWriter = consoleOut,
errWriter = errOut
)
.run()
val expectedOutput = projectDir.resolve("PklProject.deps.json")
assertThat(expectedOutput)
.hasContent(
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/fruit@1": {
"type": "remote",
"uri": "projectpackage://localhost:12110/fruit@1.0.5",
"checksums": {
"sha256": "b4ea243de781feeab7921227591e6584db5d0673340f30fab2ffe8ad5c9f75f5"
}
}
}
}
"""
.trimIndent()
)
assertThat(errOut.toString())
.isEqualTo(
"WARN: local dependency `package://localhost:12110/fruit@1.0.0` was overridden to remote dependency `package://localhost:12110/fruit@1.0.5`.\n"
)
}
@Test
fun `resolving multiple projects`(@TempDir tempDir: Path) {
tempDir.writeFile(
"project1/PklProject",
"""
amends "pkl:Project"
dependencies {
["birds"] {
uri = "package://localhost:12110/birds@0.5.0"
}
}
"""
.trimIndent()
)
tempDir.writeFile(
"project2/PklProject",
"""
amends "pkl:Project"
dependencies {
["fruit"] {
uri = "package://localhost:12110/fruit@1.1.0"
}
}
"""
.trimIndent()
)
val consoleOut = StringWriter()
val errOut = StringWriter()
CliProjectResolver(
CliBaseOptions(caCertificates = listOf(FileTestUtils.selfSignedCertificate)),
listOf(tempDir.resolve("project1"), tempDir.resolve("project2")),
consoleWriter = consoleOut,
errWriter = errOut
)
.run()
assertThat(consoleOut.toString())
.isEqualTo(
"""
$tempDir/project1/PklProject.deps.json
$tempDir/project2/PklProject.deps.json
"""
.trimIndent()
)
assertThat(tempDir.resolve("project1/PklProject.deps.json"))
.hasContent(
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/birds@0": {
"type": "remote",
"uri": "projectpackage://localhost:12110/birds@0.5.0",
"checksums": {
"sha256": "3f19ab9fcee2f44f93a75a09e531db278c6d2cd25206836c8c2c4071cd7d3118"
}
},
"package://localhost:12110/fruit@1": {
"type": "remote",
"uri": "projectpackage://localhost:12110/fruit@1.0.5",
"checksums": {
"sha256": "b4ea243de781feeab7921227591e6584db5d0673340f30fab2ffe8ad5c9f75f5"
}
}
}
}
"""
.trimIndent()
)
assertThat(tempDir.resolve("project2/PklProject.deps.json"))
.hasContent(
"""
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://localhost:12110/fruit@1": {
"type": "remote",
"uri": "projectpackage://localhost:12110/fruit@1.1.0",
"checksums": {
"sha256": "98ad9fc407a79dc3fd5595e7a29c3803ade0a6957c18ec94b8a1624360b24f01"
}
}
}
}
"""
.trimIndent()
)
}
}

View File

@@ -0,0 +1,208 @@
/**
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.cli
import com.github.ajalt.clikt.core.MissingArgument
import com.github.ajalt.clikt.core.subcommands
import java.io.StringWriter
import java.net.URI
import java.nio.file.Path
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.cli.commands.EvalCommand
import org.pkl.cli.commands.RootCommand
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
import org.pkl.commons.cli.CliTestOptions
import org.pkl.commons.readString
import org.pkl.commons.toUri
import org.pkl.commons.writeString
import org.pkl.core.Release
class CliTestRunnerTest {
@Test
fun `CliTestRunner succeed test`(@TempDir tempDir: Path) {
val code =
"""
amends "pkl:test"
facts {
["succeed"] {
8 == 8
3 == 3
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
val out = StringWriter()
val err = StringWriter()
val opts = CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions()
val runner = CliTestRunner(opts, testOpts, consoleWriter = out, errWriter = err)
runner.run()
assertThat(out.toString().stripFileAndLines(tempDir))
.isEqualTo(
"""
module test
succeed ✅
"""
.trimIndent()
)
assertThat(err.toString()).isEqualTo("")
}
@Test
fun `CliTestRunner fail test`(@TempDir tempDir: Path) {
val code =
"""
amends "pkl:test"
facts {
["fail"] {
4 == 9
"foo" == "bar"
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
val out = StringWriter()
val err = StringWriter()
val opts = CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions()
val runner = CliTestRunner(opts, testOpts, consoleWriter = out, errWriter = err)
assertThatCode { runner.run() }.hasMessage("Tests failed.")
assertThat(out.toString().stripFileAndLines(tempDir))
.isEqualTo(
"""
module test
fail ❌
4 == 9 ❌
"foo" == "bar" ❌
"""
.trimIndent()
)
assertThat(err.toString()).isEqualTo("")
}
@Test
fun `CliTestRunner JUnit reports`(@TempDir tempDir: Path) {
val code =
"""
amends "pkl:test"
facts {
["foo"] {
9 == trace(9)
"foo" == "foo"
}
["fail"] {
5 == 9
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(code).toString()
val opts = CliBaseOptions(sourceModules = listOf(input.toUri()), settings = URI("pkl:settings"))
val testOpts = CliTestOptions(junitDir = tempDir)
val runner = CliTestRunner(opts, testOpts)
assertThatCode { runner.run() }.hasMessageContaining("failed")
val junitReport = tempDir.resolve("test.xml").readString().stripFileAndLines(tempDir)
assertThat(junitReport)
.isEqualTo(
"""
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="test" tests="2" failures="1">
<testcase classname="test" name="foo"></testcase>
<testcase classname="test" name="fail">
<failure message="Fact Failure">5 == 9 ❌</failure>
</testcase>
<system-err><![CDATA[9 = 9
]]></system-err>
</testsuite>
"""
.trimIndent()
)
}
@Test
fun `CliTestRunner duplicated JUnit reports`(@TempDir tempDir: Path) {
val foo =
"""
module foo
amends "pkl:test"
facts {
["foo"] {
1 == 1
}
}
"""
.trimIndent()
val bar =
"""
module foo
amends "pkl:test"
facts {
["foo"] {
1 == 1
}
}
"""
.trimIndent()
val input = tempDir.resolve("test.pkl").writeString(foo).toString()
val input2 = tempDir.resolve("test.pkl").writeString(bar).toString()
val opts =
CliBaseOptions(
sourceModules = listOf(input.toUri(), input2.toUri()),
settings = URI("pkl:settings")
)
val testOpts = CliTestOptions(junitDir = tempDir)
val runner = CliTestRunner(opts, testOpts)
assertThatCode { runner.run() }.hasMessageContaining("failed")
}
@Test
fun `no source modules specified has same message as pkl eval`() {
val e1 = assertThrows<CliException> { CliTestRunner(CliBaseOptions(), CliTestOptions()).run() }
val e2 =
assertThrows<MissingArgument> {
val rootCommand =
RootCommand("pkl", Release.current().versionInfo(), "").subcommands(EvalCommand(""))
rootCommand.parse(listOf("eval"))
}
assertThat(e1).hasMessageContaining("Missing argument \"<modules>\"")
assertThat(e1.message!!.replace("test", "eval")).isEqualTo(e2.helpMessage())
}
private fun String.stripFileAndLines(tmpDir: Path) =
replace(tmpDir.toUri().toString(), "/tempDir/").replace(Regex(""" \(.*, line \d+\)"""), "")
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.cli.repl
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.pkl.commons.toPath
import org.pkl.core.Loggers
import org.pkl.core.SecurityManagers
import org.pkl.core.StackFrameTransformers
import org.pkl.core.module.ModuleKeyFactories
import org.pkl.core.repl.ReplRequest
import org.pkl.core.repl.ReplResponse
import org.pkl.core.repl.ReplServer
class ReplMessagesTest {
private val server =
ReplServer(
SecurityManagers.defaultManager,
Loggers.stdErr(),
listOf(ModuleKeyFactories.standardLibrary),
listOf(),
mapOf(),
mapOf(),
null,
null,
null,
"/".toPath(),
StackFrameTransformers.defaultTransformer
)
@Test
fun `run examples`() {
val examples = ReplMessages.examples
var startIndex = examples.indexOf("```")
while (startIndex != -1) {
val endIndex = examples.indexOf("```", startIndex + 3)
assertThat(endIndex).isNotEqualTo(-1)
val text =
examples
.substring(startIndex + 3, endIndex)
.lines()
.filterNot { it.contains(":force") }
.joinToString("\n")
val responses = server.handleRequest(ReplRequest.Eval("1", text, true, true))
assertThat(responses.size).isBetween(1, 9)
assertThat(responses).hasOnlyElementsOfType(ReplResponse.EvalSuccess::class.java)
startIndex = examples.indexOf("```", endIndex + 3)
}
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.cli
import java.nio.charset.StandardCharsets
import java.nio.file.Path
import org.pkl.commons.createParentDirectories
import org.pkl.commons.writeString
fun Path.writeFile(fileName: String, contents: String): Path {
return resolve(fileName).apply {
createParentDirectories()
writeString(contents, StandardCharsets.UTF_8)
}
}
fun Path.writeEmptyFile(fileName: String): Path = writeFile(fileName, "")