Skip to content

Commit

Permalink
Validate that inline snapshot source is within source directory. (#6)
Browse files Browse the repository at this point in the history
* Validate that inline snapshot source is within source directory.

* Update modules/core/src/main/scala/com/siriusxm/snapshot4s/SnapshotConfigError.scala

Co-authored-by: Michał Pawlik <[email protected]>

* Add comments and extend Exception.

---------

Co-authored-by: Michał Pawlik <[email protected]>
  • Loading branch information
zainab-ali and majk-p authored Aug 12, 2024
1 parent 3f6d95b commit 7f0eb19
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 10 deletions.
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ lazy val core = (projectMatrix in file("modules/core"))
.jvmPlatform(
scalaVersions = scalaVersions,
libraryDependencies += "com.lihaoyi" %% "os-lib" % Versions.oslib
).jsPlatform(scalaVersions = scalaVersions,
// module support is required to run tests
Test / scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) }
)
.jsPlatform(scalaVersions = scalaVersions)

lazy val munit = (projectMatrix in file("modules/munit"))
.settings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,15 @@ object InlineSnapshot {
changeFile.write(actualStr)
}

private def relativeSourceFilePath(
private[snapshot4s] def relativeSourceFilePath(
sourceFile: String,
config: SnapshotConfig
): RelPath = {
val baseDirectory = config.sourceDirectory
val sourceFilePath = Path(sourceFile)
sourceFilePath.relativeTo(baseDirectory)
sourceFilePath.relativeTo(baseDirectory).getOrElse {
throw new SnapshotConfigUnsupportedError(config)
}
}

// See the Scala 2.13 compiler for the source of the warning we're ignoring:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@ private[snapshot4s] trait PathApi {

private[snapshot4s] def read(): String
private[snapshot4s] def write(contents: String): Unit
private[snapshot4s] def relativeTo(baseDirectory: Path): RelPath
private[snapshot4s] def relativeTo(baseDirectory: Path): Option[RelPath]
private[snapshot4s] def /(path: RelPath): Path

private[snapshot4s] def exists(): Boolean

private[snapshot4s] def value: String
}

trait PathCompanionApi {
def apply(path: String): Path
}

private[snapshot4s] trait RelPathApi {
private[snapshot4s] def value: String
}

private[snapshot4s] trait RelPathCompanionApi {
def apply(path: String): RelPath
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2024 SiriusXM
*
* 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
*
* http://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 snapshot4s

private final class SnapshotConfigUnsupportedError(config: SnapshotConfig) extends Exception({
s"""Your project setup is not supported by snapshot4s. We encourage you to raise an issue at https://github.com/siriusxm/snapshot4s.

We have detected the following configuration:
- sourceDirectory: ${config.sourceDirectory.value}
- resourceDirectory: ${config.resourceDirectory.value}
- outputDirectory: ${config.outputDirectory.value}
"""
})
13 changes: 11 additions & 2 deletions modules/core/src/main/scalajs/com/siriusxm/snapshot4s/Path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,23 @@ final class Path private (override val toString: String) extends PathApi {
)
}

private[snapshot4s] def relativeTo(baseDirectory: Path): RelPath =
RelPath(facade.relative(baseDirectory.toString, toString))
private[snapshot4s] def relativeTo(baseDirectory: Path): Option[RelPath] = {
val text = facade.relative(baseDirectory.toString, toString)
// Check that this path is in a subdirectory of the base directory.
if (text.startsWith("../")) {
None
} else {
Some(RelPath(text))
}
}

private[snapshot4s] def /(path: RelPath): Path =
Path(facade.join(toString, path.toString))

private[snapshot4s] def exists(): Boolean =
facade.exists(toString)

private[snapshot4s] def value: String = toString
}

object Path extends PathCompanionApi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package snapshot4s

private[snapshot4s] final class RelPath private (override val toString: String)
private[snapshot4s] final class RelPath private (override val toString: String) extends RelPathApi {
private[snapshot4s] def value: String = toString
}

private[snapshot4s] object RelPath extends RelPathCompanionApi {
def apply(path: String): RelPath = new RelPath(path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ final class Path private[snapshot4s] (val osPath: os.Path) extends PathApi {
private[snapshot4s] def write(contents: String): Unit =
os.write.over(osPath, contents, createFolders = true)

private[snapshot4s] def relativeTo(baseDirectory: Path): RelPath =
new RelPath(osPath.relativeTo(baseDirectory.osPath))
private[snapshot4s] def relativeTo(baseDirectory: Path): Option[RelPath] =
if (osPath.startsWith(baseDirectory.osPath))
Some(new RelPath(osPath.relativeTo(baseDirectory.osPath)))
else None

private[snapshot4s] def /(path: RelPath): Path =
new Path(osPath / path.osPath)

def exists(): Boolean = os.exists(osPath)

private[snapshot4s] def value: String = osPath.toString
}

object Path extends PathCompanionApi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package snapshot4s

private[snapshot4s] final class RelPath(val osPath: os.PathChunk)
private[snapshot4s] final class RelPath(val osPath: os.PathChunk) extends RelPathApi {
private[snapshot4s] def value: String = osPath.toString
}

object RelPath extends RelPathCompanionApi {
def apply(path: String): RelPath =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package snapshot4s

import cats.effect.IO
import weaver.*

object InlineSnapshotSpec extends SimpleIOSuite {

pureTest("calculates relative paths correctly") {
val config = new SnapshotConfig(
resourceDirectory = Path("/path/to/resources"),
outputDirectory = Path("/path/to/output"),
sourceDirectory = Path("/path/to/sources")
)
val relativePath =
InlineSnapshot.relativeSourceFilePath("/path/to/sources/TestFile.scala", config)
expect.eql(relativePath.value, "TestFile.scala")
}

test("raises errors if the source directory is calculated incorrectly") {
val config = new SnapshotConfig(
resourceDirectory = Path("/path/to/resources"),
outputDirectory = Path("/path/to/output"),
sourceDirectory = Path("/wrong/path/to/sources")
)
val result =
IO(InlineSnapshot.relativeSourceFilePath("/path/to/sources/TestFile.scala", config))
val message =
"""Your project setup is not supported by snapshot4s. We encourage you to raise an issue at https://github.com/siriusxm/snapshot4s.
We have detected the following configuration:
- sourceDirectory: /wrong/path/to/sources
- resourceDirectory: /path/to/resources
- outputDirectory: /path/to/output
"""
result.attempt.map { result =>
matches(result) { case Left(err) =>
expect.eql(err.getMessage, message)
}
}

}
}

0 comments on commit 7f0eb19

Please sign in to comment.