mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 16:18:23 +03:00
File Reads for the Language Server (#559)
File Reads for the Language Server
This commit is contained in:
parent
75127d07b9
commit
fe471314ec
@ -1111,9 +1111,9 @@ null
|
|||||||
|
|
||||||
##### Errors
|
##### Errors
|
||||||
|
|
||||||
- **FileSystemError(errorCode=1000)** This error signals generic file system errors.
|
- [`FileSystemError`](#filesystemerror) to signal a generic, unrecoverable file-system error.
|
||||||
- **ContentRootNotFoundError(errorCode=1001)** The error informs that the requested content root cannot be found.
|
- [`ContentRootNotFoundError`](#contentrootnotfounderror) to signal that the requested content root cannot be found.
|
||||||
- **AccessDeniedError(errorCode=1002)** It signals that a user doesn't have access to a resource.
|
- [`AccessDeniedError`](#accessdeniederror) to signal that a user doesn't have access to a resource.
|
||||||
|
|
||||||
#### `file/read`
|
#### `file/read`
|
||||||
This requests that the file manager component reads the contents of a specified
|
This requests that the file manager component reads the contents of a specified
|
||||||
@ -1142,7 +1142,11 @@ return the contents from the in-memory buffer rather than the file on disk.
|
|||||||
```
|
```
|
||||||
|
|
||||||
##### Errors
|
##### Errors
|
||||||
TBC
|
|
||||||
|
- [`FileSystemError`](#filesystemerror) to signal a generic, unrecoverable file-system error.
|
||||||
|
- [`ContentRootNotFoundError`](#contentrootnotfounderror) to signal that the requested content root cannot be found.
|
||||||
|
- [`AccessDeniedError`](#accessdeniederror) to signal that a user doesn't have access to a resource.
|
||||||
|
- [`FileNotFound`](#filenotfound) informs that file cannot be found.
|
||||||
|
|
||||||
#### `file/create`
|
#### `file/create`
|
||||||
This request asks the file manager to create the specified file system object.
|
This request asks the file manager to create the specified file system object.
|
||||||
@ -1692,3 +1696,43 @@ TBC
|
|||||||
### Errors - Language Server
|
### Errors - Language Server
|
||||||
The language server component also has its own set of errors. This section is
|
The language server component also has its own set of errors. This section is
|
||||||
not a complete specification and will be updated as new errors are added.
|
not a complete specification and will be updated as new errors are added.
|
||||||
|
|
||||||
|
##### `FileSystemError`
|
||||||
|
This error signals generic file system errors.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
"error" : {
|
||||||
|
"code" : 1000,
|
||||||
|
"message" : "File '/foo/bar' exists but is a directory"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `ContentRootNotFoundError`
|
||||||
|
The error informs that the requested content root cannot be found.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
"error" : {
|
||||||
|
"code" : 1001,
|
||||||
|
"message" : "Content root not found"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `AccessDeniedError`
|
||||||
|
It signals that a user doesn't have access to a resource.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
"error" : {
|
||||||
|
"code" : 1002,
|
||||||
|
"message" : "Access denied"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `FileNotFound`
|
||||||
|
It signals that requested file doesn't exist.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
"error" : {
|
||||||
|
"code" : 1003,
|
||||||
|
"message" : "File not found"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -7,15 +7,10 @@ import akka.pattern.ask
|
|||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import org.enso.languageserver.ClientApi._
|
import org.enso.languageserver.ClientApi._
|
||||||
import org.enso.languageserver.data.{CapabilityRegistration, Client}
|
import org.enso.languageserver.data.{CapabilityRegistration, Client}
|
||||||
import org.enso.languageserver.filemanager.FileManagerApi.{
|
import org.enso.languageserver.filemanager.FileManagerApi.{FileRead, _}
|
||||||
FileSystemError,
|
|
||||||
FileWrite,
|
|
||||||
FileWriteParams
|
|
||||||
}
|
|
||||||
import org.enso.languageserver.filemanager.FileManagerProtocol.FileWriteResult
|
import org.enso.languageserver.filemanager.FileManagerProtocol.FileWriteResult
|
||||||
import org.enso.languageserver.filemanager.{
|
import org.enso.languageserver.filemanager.{
|
||||||
FileManagerProtocol,
|
FileManagerProtocol,
|
||||||
FileSystemFailure,
|
|
||||||
FileSystemFailureMapper
|
FileSystemFailureMapper
|
||||||
}
|
}
|
||||||
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
import org.enso.languageserver.jsonrpc.Errors.ServiceError
|
||||||
@ -69,6 +64,7 @@ object ClientApi {
|
|||||||
.registerRequest(AcquireCapability)
|
.registerRequest(AcquireCapability)
|
||||||
.registerRequest(ReleaseCapability)
|
.registerRequest(ReleaseCapability)
|
||||||
.registerRequest(FileWrite)
|
.registerRequest(FileWrite)
|
||||||
|
.registerRequest(FileRead)
|
||||||
.registerNotification(ForceReleaseCapability)
|
.registerNotification(ForceReleaseCapability)
|
||||||
.registerNotification(GrantCapability)
|
.registerNotification(GrantCapability)
|
||||||
|
|
||||||
@ -123,22 +119,57 @@ class ClientController(
|
|||||||
server ! LanguageProtocol.ReleaseCapability(clientId, params.id)
|
server ! LanguageProtocol.ReleaseCapability(clientId, params.id)
|
||||||
sender ! ResponseResult(ReleaseCapability, id, Unused)
|
sender ! ResponseResult(ReleaseCapability, id, Unused)
|
||||||
|
|
||||||
case Request(FileWrite, id, params: FileWriteParams) =>
|
case Request(FileWrite, id, params: FileWrite.Params) =>
|
||||||
(server ? FileManagerProtocol.FileWrite(params.path, params.content))
|
writeFile(webActor, id, params)
|
||||||
.onComplete {
|
|
||||||
case Success(FileWriteResult(Right(()))) =>
|
|
||||||
webActor ! ResponseResult(FileWrite, id, Unused)
|
|
||||||
|
|
||||||
case Success(FileWriteResult(Left(failure))) =>
|
case Request(FileRead, id, params: FileRead.Params) =>
|
||||||
webActor ! ResponseError(
|
readFile(webActor, id, params)
|
||||||
Some(id),
|
|
||||||
FileSystemFailureMapper.mapFailure(failure)
|
|
||||||
)
|
|
||||||
|
|
||||||
case Failure(th) =>
|
}
|
||||||
log.error("An exception occurred during writing to a file", th)
|
|
||||||
webActor ! ResponseError(Some(id), ServiceError)
|
private def readFile(
|
||||||
}
|
webActor: ActorRef,
|
||||||
|
id: Id,
|
||||||
|
params: FileRead.Params
|
||||||
|
): Unit = {
|
||||||
|
(server ? FileManagerProtocol.FileRead(params.path)).onComplete {
|
||||||
|
case Success(
|
||||||
|
FileManagerProtocol.FileReadResult(Right(content: String))
|
||||||
|
) =>
|
||||||
|
webActor ! ResponseResult(FileRead, id, FileRead.Result(content))
|
||||||
|
|
||||||
|
case Success(FileManagerProtocol.FileReadResult(Left(failure))) =>
|
||||||
|
webActor ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemFailureMapper.mapFailure(failure)
|
||||||
|
)
|
||||||
|
|
||||||
|
case Failure(th) =>
|
||||||
|
log.error("An exception occurred during reading a file", th)
|
||||||
|
webActor ! ResponseError(Some(id), ServiceError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def writeFile(
|
||||||
|
webActor: ActorRef,
|
||||||
|
id: Id,
|
||||||
|
params: FileWrite.Params
|
||||||
|
): Unit = {
|
||||||
|
(server ? FileManagerProtocol.FileWrite(params.path, params.contents))
|
||||||
|
.onComplete {
|
||||||
|
case Success(FileWriteResult(Right(()))) =>
|
||||||
|
webActor ! ResponseResult(FileWrite, id, Unused)
|
||||||
|
|
||||||
|
case Success(FileWriteResult(Left(failure))) =>
|
||||||
|
webActor ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemFailureMapper.mapFailure(failure)
|
||||||
|
)
|
||||||
|
|
||||||
|
case Failure(th) =>
|
||||||
|
log.error("An exception occurred during writing to a file", th)
|
||||||
|
webActor ! ResponseError(Some(id), ServiceError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import akka.actor.{Actor, ActorLogging, ActorRef, Stash}
|
|||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import org.enso.languageserver.data._
|
import org.enso.languageserver.data._
|
||||||
import org.enso.languageserver.filemanager.FileManagerProtocol.{
|
import org.enso.languageserver.filemanager.FileManagerProtocol.{
|
||||||
|
FileRead,
|
||||||
|
FileReadResult,
|
||||||
FileWrite,
|
FileWrite,
|
||||||
FileWriteResult
|
FileWriteResult
|
||||||
}
|
}
|
||||||
@ -134,5 +136,19 @@ class LanguageServer(config: Config, fs: FileSystemApi[IO])
|
|||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
sender ! FileWriteResult(result)
|
sender ! FileWriteResult(result)
|
||||||
|
|
||||||
|
case FileRead(path) =>
|
||||||
|
val result =
|
||||||
|
for {
|
||||||
|
rootPath <- config.findContentRoot(path.rootId)
|
||||||
|
content <- fs.read(path.toFile(rootPath)).unsafeRunSync()
|
||||||
|
} yield content
|
||||||
|
|
||||||
|
sender ! FileReadResult(result)
|
||||||
}
|
}
|
||||||
|
/* Note [Usage of unsafe methods]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
It invokes side-effecting function, all exceptions are caught and
|
||||||
|
explicitly returned as left side of disjunction.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,30 @@ import org.enso.languageserver.jsonrpc.{
|
|||||||
object FileManagerApi {
|
object FileManagerApi {
|
||||||
|
|
||||||
case object FileWrite extends Method("file/write") {
|
case object FileWrite extends Method("file/write") {
|
||||||
|
|
||||||
|
case class Params(path: Path, contents: String)
|
||||||
|
|
||||||
implicit val hasParams = new HasParams[this.type] {
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
type Params = FileWriteParams
|
type Params = FileWrite.Params
|
||||||
}
|
}
|
||||||
implicit val hasResult = new HasResult[this.type] {
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
type Result = Unused.type
|
type Result = Unused.type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class FileWriteParams(path: Path, content: String)
|
case object FileRead extends Method("file/read") {
|
||||||
|
|
||||||
|
case class Params(path: Path)
|
||||||
|
|
||||||
|
case class Result(contents: String)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = FileRead.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = FileRead.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case class FileSystemError(override val message: String)
|
case class FileSystemError(override val message: String)
|
||||||
extends Error(1000, message)
|
extends Error(1000, message)
|
||||||
@ -34,4 +49,6 @@ object FileManagerApi {
|
|||||||
|
|
||||||
case object AccessDeniedError extends Error(1002, "Access denied")
|
case object AccessDeniedError extends Error(1002, "Access denied")
|
||||||
|
|
||||||
|
case object FileNotFoundError extends Error(1003, "File not found")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,18 @@ object FileManagerProtocol {
|
|||||||
*/
|
*/
|
||||||
case class FileWriteResult(result: Either[FileSystemFailure, Unit])
|
case class FileWriteResult(result: Either[FileSystemFailure, Unit])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the Language Server read a file.
|
||||||
|
*
|
||||||
|
* @param path a path to a file
|
||||||
|
*/
|
||||||
|
case class FileRead(path: Path)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a result of reading a file.
|
||||||
|
*
|
||||||
|
* @param result either file system failure or content of a file
|
||||||
|
*/
|
||||||
|
case class FileReadResult(result: Either[FileSystemFailure, String])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package org.enso.languageserver.filemanager
|
package org.enso.languageserver.filemanager
|
||||||
|
|
||||||
import java.io.{File, IOException}
|
import java.io.{File, FileNotFoundException, IOException}
|
||||||
import java.nio.file._
|
import java.nio.file._
|
||||||
|
|
||||||
import cats.effect.Sync
|
import cats.effect.Sync
|
||||||
@ -25,20 +25,33 @@ class FileSystem[F[_]: Sync] extends FileSystemApi[F] {
|
|||||||
file: File,
|
file: File,
|
||||||
content: String
|
content: String
|
||||||
): F[Either[FileSystemFailure, Unit]] =
|
): F[Either[FileSystemFailure, Unit]] =
|
||||||
Sync[F].delay { writeStringToFile(file, content) }
|
Sync[F].delay {
|
||||||
|
Either
|
||||||
|
.catchOnly[IOException] {
|
||||||
|
FileUtils.write(file, content, "UTF-8")
|
||||||
|
}
|
||||||
|
.leftMap(errorHandling)
|
||||||
|
}
|
||||||
|
|
||||||
private def writeStringToFile(
|
/**
|
||||||
file: File,
|
* Reads the contents of a textual file.
|
||||||
content: String
|
*
|
||||||
): Either[FileSystemFailure, Unit] =
|
* @param file path to the file
|
||||||
Either
|
* @return either [[FileSystemFailure]] or the content of a file as a String
|
||||||
.catchOnly[IOException](
|
*/
|
||||||
FileUtils.write(file, content, "UTF-8")
|
override def read(file: File): F[Either[FileSystemFailure, String]] =
|
||||||
)
|
Sync[F].delay {
|
||||||
.leftMap {
|
Either
|
||||||
case _: AccessDeniedException => AccessDenied
|
.catchOnly[IOException] {
|
||||||
case ex => GenericFileSystemFailure(ex.getMessage)
|
FileUtils.readFileToString(file, "UTF-8")
|
||||||
}
|
}
|
||||||
.map(_ => ())
|
.leftMap(errorHandling)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val errorHandling: IOException => FileSystemFailure = {
|
||||||
|
case _: FileNotFoundException => FileNotFound
|
||||||
|
case _: AccessDeniedException => AccessDenied
|
||||||
|
case ex => GenericFileSystemFailure(ex.getMessage)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,19 @@ trait FileSystemApi[F[_]] {
|
|||||||
*
|
*
|
||||||
* @param file path to the file
|
* @param file path to the file
|
||||||
* @param content a textual content of the file
|
* @param content a textual content of the file
|
||||||
* @return either FileSystemFailure or Unit
|
* @return either [[FileSystemFailure]] or Unit
|
||||||
*/
|
*/
|
||||||
def write(
|
def write(
|
||||||
file: File,
|
file: File,
|
||||||
content: String
|
content: String
|
||||||
): F[Either[FileSystemFailure, Unit]]
|
): F[Either[FileSystemFailure, Unit]]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the contents of a textual file.
|
||||||
|
*
|
||||||
|
* @param file path to the file
|
||||||
|
* @return either [[FileSystemFailure]] or the content of a file as a String
|
||||||
|
*/
|
||||||
|
def read(file: File): F[Either[FileSystemFailure, String]]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,11 @@ case object ContentRootNotFound extends FileSystemFailure
|
|||||||
*/
|
*/
|
||||||
case object AccessDenied extends FileSystemFailure
|
case object AccessDenied extends FileSystemFailure
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that the file cannot be found.
|
||||||
|
*/
|
||||||
|
case object FileNotFound extends FileSystemFailure
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals file system specific errors.
|
* Signals file system specific errors.
|
||||||
*
|
*
|
||||||
|
@ -3,16 +3,24 @@ package org.enso.languageserver.filemanager
|
|||||||
import org.enso.languageserver.filemanager.FileManagerApi.{
|
import org.enso.languageserver.filemanager.FileManagerApi.{
|
||||||
AccessDeniedError,
|
AccessDeniedError,
|
||||||
ContentRootNotFoundError,
|
ContentRootNotFoundError,
|
||||||
|
FileNotFoundError,
|
||||||
FileSystemError
|
FileSystemError
|
||||||
}
|
}
|
||||||
import org.enso.languageserver.jsonrpc.Error
|
import org.enso.languageserver.jsonrpc.Error
|
||||||
|
|
||||||
object FileSystemFailureMapper {
|
object FileSystemFailureMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps [[FileSystemFailure]] into JSON RPC error.
|
||||||
|
*
|
||||||
|
* @param fileSystemFailure file system specific failure
|
||||||
|
* @return JSON RPC error
|
||||||
|
*/
|
||||||
def mapFailure(fileSystemFailure: FileSystemFailure): Error =
|
def mapFailure(fileSystemFailure: FileSystemFailure): Error =
|
||||||
fileSystemFailure match {
|
fileSystemFailure match {
|
||||||
case ContentRootNotFound => ContentRootNotFoundError
|
case ContentRootNotFound => ContentRootNotFoundError
|
||||||
case AccessDenied => AccessDeniedError
|
case AccessDenied => AccessDeniedError
|
||||||
|
case FileNotFound => FileNotFoundError
|
||||||
case GenericFileSystemFailure(reason) => FileSystemError(reason)
|
case GenericFileSystemFailure(reason) => FileSystemError(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ class WebSocketServerTest
|
|||||||
"rootId": $testContentRootId,
|
"rootId": $testContentRootId,
|
||||||
"segments": [ "foo", "bar", "baz.txt" ]
|
"segments": [ "foo", "bar", "baz.txt" ]
|
||||||
},
|
},
|
||||||
"content": "123456789"
|
"contents": "123456789"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
@ -267,7 +267,7 @@ class WebSocketServerTest
|
|||||||
"rootId": ${UUID.randomUUID()},
|
"rootId": ${UUID.randomUUID()},
|
||||||
"segments": [ "foo", "bar", "baz.txt" ]
|
"segments": [ "foo", "bar", "baz.txt" ]
|
||||||
},
|
},
|
||||||
"content": "123456789"
|
"contents": "123456789"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
@ -283,6 +283,74 @@ class WebSocketServerTest
|
|||||||
client.expectNoMessage()
|
client.expectNoMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"read a file content" in {
|
||||||
|
val client = new WsTestClient(address)
|
||||||
|
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "file/write",
|
||||||
|
"id": 4,
|
||||||
|
"params": {
|
||||||
|
"path": {
|
||||||
|
"rootId": $testContentRootId,
|
||||||
|
"segments": [ "foo.txt" ]
|
||||||
|
},
|
||||||
|
"contents": "123456789"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 4,
|
||||||
|
"result": null
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "file/read",
|
||||||
|
"id": 5,
|
||||||
|
"params": {
|
||||||
|
"path": {
|
||||||
|
"rootId": $testContentRootId,
|
||||||
|
"segments": [ "foo.txt" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 5,
|
||||||
|
"result": { "contents": "123456789" }
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
"return FileNotFoundError if a file doesn't exist" in {
|
||||||
|
val client = new WsTestClient(address)
|
||||||
|
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "file/read",
|
||||||
|
"id": 6,
|
||||||
|
"params": {
|
||||||
|
"path": {
|
||||||
|
"rootId": $testContentRootId,
|
||||||
|
"segments": [ "bar.txt" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 6,
|
||||||
|
"error" : {
|
||||||
|
"code" : 1003,
|
||||||
|
"message" : "File not found"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class WsTestClient(address: String) {
|
class WsTestClient(address: String) {
|
||||||
|
@ -48,6 +48,26 @@ class FileSystemSpec extends AnyFlatSpec with Matchers {
|
|||||||
readTxtFile(path) shouldBe content
|
readTxtFile(path) shouldBe content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "return FileNotFound failure if the file doesn't exist" in new TestCtx {
|
||||||
|
//given
|
||||||
|
val path = Paths.get(testDirPath.toString, "foo.txt")
|
||||||
|
//when
|
||||||
|
val result = objectUnderTest.read(path.toFile).unsafeRunSync()
|
||||||
|
//then
|
||||||
|
result shouldBe Left(FileNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "read a file content" in new TestCtx {
|
||||||
|
//given
|
||||||
|
val path = Paths.get(testDirPath.toString, "foo.txt")
|
||||||
|
val content = "123456789"
|
||||||
|
objectUnderTest.write(path.toFile, content).unsafeRunSync()
|
||||||
|
//when
|
||||||
|
val result = objectUnderTest.read(path.toFile).unsafeRunSync()
|
||||||
|
//then
|
||||||
|
result shouldBe Right(content)
|
||||||
|
}
|
||||||
|
|
||||||
def readTxtFile(path: Path): String = {
|
def readTxtFile(path: Path): String = {
|
||||||
val buffer = Source.fromFile(path.toFile)
|
val buffer = Source.fromFile(path.toFile)
|
||||||
val content = buffer.getLines().mkString
|
val content = buffer.getLines().mkString
|
||||||
|
Loading…
Reference in New Issue
Block a user