Skip to content

Commit

Permalink
feat: CacheFS (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
pawelhertman committed Feb 7, 2023
1 parent 875785c commit ea21718
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 6 deletions.
13 changes: 7 additions & 6 deletions .github/workflows/release.yml
Expand Up @@ -9,21 +9,22 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: 14
node-version-file: '.nvmrc'
cache: 'npm'
- name: Semantic Release
id: release
uses: cycjimmy/semantic-release-action@v2
uses: cycjimmy/semantic-release-action@v3
env:
GITHUB_TOKEN: ${{ github.token }}
NPM_TOKEN: ${{ secrets.OSS_NPM_TOKEN }}
with:
extra_plugins: |
@semantic-release/changelog@5.0.1
@semantic-release/git@9.0.0
@semantic-release/changelog@6
@semantic-release/git@10
- name: Teams notification
uses: toko-bifrost/ms-teams-deploy-card@3.1.2
if: always()
Expand Down
63 changes: 63 additions & 0 deletions src/components/CacheFS.brs
@@ -0,0 +1,63 @@
' @import /components/rokuComponents/EVPDigest.brs
function CacheFS() as Object
prototype = {}

prototype._PREFIX = "cachefs:/"

prototype._digest = Invalid
prototype._fileSystem = Invalid

_constructor = function (m as Object) as Object
m._digest = EVPDigest()
m._digest.setup("sha1")
m._fileSystem = CreateObject("roFileSystem")

return m
end function

' Reads data stored in cachefs under the specific key
' @param {String} key
' @returns {Object} Invalid if key is incorrect or is not stored
prototype.read = function (key as String) as Object
if (key = "") then return Invalid

filePath = m._PREFIX + m._hash(key)
if (NOT m._fileSystem.exists(filePath)) then return Invalid

content = ReadAsciiFile(filePath)

return ParseJson(content)
end function

' Writes data into cachefs under the specific key
' @param {String} key
' @param {Object} data - any value acceptable by native FormatJson function except Invalid
' @returns {Boolean} false if data is not parseable, Invalid or if storing failed
prototype.write = function (key as String, data as Object) as Boolean
if (key = "" OR data = Invalid) then return false

content = FormatJson(data)
if (content = "") then return false

return WriteAsciiFile(m._PREFIX + m._hash(key), content)
end function

' Deletes data from cachefs under the specific key
' @param {String} key
' @returns {Boolean} true if data successfully removed
prototype.delete = function (key as String) as Boolean
if (key = "") then return false

return DeleteFile(m._PREFIX + m._hash(key))
end function

' @private
prototype._hash = function (text as String) as String
byteArray = CreateObject("roByteArray")
byteArray.fromAsciiString(text)

return m._digest.process(byteArray)
end function

return _constructor(prototype)
end function
135 changes: 135 additions & 0 deletions src/components/_tests/CacheFS.test.brs
@@ -0,0 +1,135 @@
' @import /components/KopytkoTestSuite.brs from @dazn/kopytko-unit-testing-framework
' @mock /components/rokuComponents/EVPDigest.brs
function TestSuite__CacheFS() as Object
ts = KopytkoTestSuite()
ts.name = "CacheFS"

ts.setBeforeEach(sub (ts as Object)
m.__mocks = {}
m.__mocks.EVPDigest = {
process: {
returnValue: "stringValue",
},
}

ts.__dataKey = "example key"
end sub)

ts.setAfterEach(sub (ts as Object)
CacheFS().delete(ts.__dataKey)
end sub)

ts.addTest("constructor creates EVPDigest and sets sha1 algorithm", function (ts as Object) as String
' When
CacheFS()

' Then
return ts.assertMethodWasCalled("EVPDigest.setup", { digestType: "sha1" })
end function)

ts.addParameterizedTests([
{ super: "data" },
[1, 2, 3],
"",
777,
], "read returns data stored via write method", function (ts as Object, data as Object) as String
' Given
cache = CacheFS()
if (NOT cache.write(ts.__dataKey, data))
return ts.fail("Couldn't write data to CacheFS")
end if

' When
returnedData = cache.read(ts.__dataKey)

' Then
return ts.assertEqual(returnedData, data)
end function)

ts.addTest("read returns invalid in case of empty data under the given key", function (ts as Object) as String
' Given
cache = CacheFS()

' When
returnedData = cache.read("any key")

' Then
return ts.assertInvalid(returnedData)
end function)

ts.addTest("read returns invalid in case of empty key passed", function (ts as Object) as String
' Given
cache = CacheFS()

' When
returnedData = cache.read("")

' Then
return ts.assertInvalid(returnedData)
end function)

ts.addTest("write returns false in case of empty key passed", function (ts as Object) as String
' Given
cache = CacheFS()

' When
returnedData = cache.write("", { any: "data" })

' Then
return ts.assertFalse(returnedData)
end function)

ts.addTest("write returns false in case of invalid data passed", function (ts as Object) as String
' Given
cache = CacheFS()

' When
returnedData = cache.write(ts.__dataKey, Invalid)

' Then
return ts.assertFalse(returnedData)
end function)

ts.addTest("write returns false in case of non-parseable data passed", function (ts as Object) as String
' Given
cache = CacheFS()

' When
returnedData = cache.write(ts.__dataKey, CreateObject("roDateTime"))

' Then
return ts.assertFalse(returnedData)
end function)

ts.addTest("delete deletes data stored under the given key", function (ts as Object) as String
' Given
data = { super: "data" }

cache = CacheFS()
if (NOT cache.write(ts.__dataKey, data))
return ts.fail("Couldn't write data to CacheFS")
end if

' When
if (NOT cache.delete(ts.__dataKey))
return ts.fail("Couldn't delete data from CacheFS")
end if
returnedData = cache.read(ts.__dataKey)

' Then
return ts.assertInvalid(returnedData)
end function)

ts.addTest("delete returns false in case of empty key passed", function (ts as Object) as String
' Given
cache = CacheFS()

' When
result = cache.delete("")

' Then
return ts.assertFalse(result)
end function)

return ts
end function
5 changes: 5 additions & 0 deletions src/components/rokuComponents/EVPDigest.brs
@@ -0,0 +1,5 @@
' Wrapper function for creating native roDateTime component.
' @class
function EVPDigest() as Object
return CreateObject("roEVPDigest")
end function
26 changes: 26 additions & 0 deletions src/components/rokuComponents/_mocks/EVPDigest.mock.brs
@@ -0,0 +1,26 @@
' @import /components/_mocks/Mock.brs from @dazn/kopytko-unit-testing-framework

' @returns {Mock}
function EVPDigest() as Object
return Mock({
testComponent: m,
name: "EVPDigest",
methods: {
setup: function (digestType as String) as Integer
return m.setupMock("setup", { digestType: digestType }, "Integer")
end function,
reinit: function () as Integer
return m.reinitMock("reinit", {}, "Integer")
end function,
process: function (bytes as Object) as String
return m.processMock("process", { bytes: bytes }, "String")
end function,
update: sub (bytes as Object)
m.updateMock("update", { bytes: bytes })
end sub,
final: function () as String
return m.finalMock("final", {}, "String")
end function,
},
})
end function

0 comments on commit ea21718

Please sign in to comment.