abstract backend; add mock backend and testing (#57)

abstract backend; add mock backend and testing

move bookmarks to backend wrapper

move bookmarks to backend wrapper

move bookmarks to backend wrapper

working version

cleanup

add mock backend

add nimble task & test folder

add evn.sh

make test run; implement bookmark method in the mock

add nim unit test

removing duplicated statement that was causing tests to run twice

re-enable other initilizations in the status object; support specifying backend in the constructor

update bookmark test

update bookmark test

update nimble and makefile

update nimble and makefile

update bookmarks test

remove old browser file
This commit is contained in:
Iuri Matias 2021-09-28 17:00:43 -04:00 committed by GitHub
parent bcc242a3cf
commit 5285cf7d8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 315 additions and 18 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
nimcache
vendor/.nimble
/bottles/
/test_nim/build

View File

@ -22,7 +22,8 @@ BUILD_SYSTEM_DIR := vendor/nimbus-build-system
status-go \
update \
build_ctest \
ctest
ctest \
test
ifeq ($(NIM_PARAMS),)
# "variables.mk" was not included, so we update the submodules.
@ -64,7 +65,6 @@ else
LIBSTATUS_EXT := so
endif
ifeq ($(detected_OS),Darwin)
bottles/openssl:
./scripts/fetch-brew-bottle.sh openssl
@ -97,7 +97,9 @@ NIM_PARAMS += --outdir:./build
STATUSGO := vendor/status-go/build/bin/libstatus.$(LIBSTATUS_EXT)
STATUSGO_LIBDIR := $(shell pwd)/$(shell dirname "$(STATUSGO)")
export STATUSGO
export STATUSGO_LIBDIR
export LIBSTATUS_EXT
status-go: $(STATUSGO)
$(STATUSGO): | deps
@ -128,4 +130,7 @@ ctest: | build_ctest
clean: | clean-common
rm -rf bin/* node_modules bottles/* pkg/* tmp/* $(STATUSGO)
test:
$(ENV_SCRIPT) nimble tests
endif # "variables.mk" was not included

31
backends/backend.nim Normal file
View File

@ -0,0 +1,31 @@
import backend_type
import backend_wrapper
export backend_wrapper
from statusgo/types as statusgo_types import StatusGoBackend
from mock/types as mock_types import MockBackend
export StatusGoBackend
export MockBackend
import base/bookmarks
import statusgo/statusgo_instance
export newStatusGoBackendInstance
import mock/mock_instance
export newMockBackendInstance
import statusgo/bookmark as statusgo_bookmark
import mock/bookmark as mock_bookmark
from bookmarks as bookmarks_methods import storeBookmark, updateBookmark, getBookmarks, deleteBookmark
export storeBookmark, updateBookmark, getBookmarks, deleteBookmark
method loadBackend*(self: BackendWrapper, name: string) =
if name == "statusgo":
self.backend = newStatusGoBackendInstance()
if name == "mock":
self.backend = newMockBackendInstance()
else:
raise newException(ValueError, "unknown backend")

View File

@ -0,0 +1 @@
type Backend* = ref object of RootObj

View File

@ -0,0 +1,7 @@
import backend_type
type BackendWrapper* = ref object
backend*: Backend
proc newBackendWrapperInstance*(): BackendWrapper =
result = BackendWrapper()

View File

@ -0,0 +1,14 @@
import ../../types/[bookmark]
import ../backend_type
method storeBookmark*(self: Backend, bookmark: Bookmark): Bookmark =
raise newException(ValueError, "No implementation available")
method updateBookmark*(self: Backend, originalUrl: string, bookmark: Bookmark) =
raise newException(ValueError, "No implementation available")
method getBookmarks*(self: Backend): seq[Bookmark] =
raise newException(ValueError, "No implementation available")
method deleteBookmark*(self: Backend, url: string) =
raise newException(ValueError, "No implementation available")

2
backends/base/main.nim Normal file
View File

@ -0,0 +1,2 @@
import bookmark
export bookmark

15
backends/bookmarks.nim Normal file
View File

@ -0,0 +1,15 @@
import backend_type, backend_wrapper
import base/bookmarks
import ../types/[bookmark]
proc storeBookmark*(self: BackendWrapper, bookmark: Bookmark): Bookmark =
self.backend.storeBookmark(bookmark)
proc updateBookmark*(self: BackendWrapper, originalUrl: string, bookmark: Bookmark) =
self.backend.updateBookmark(originalUrl, bookmark)
proc getBookmarks*(self: BackendWrapper): seq[Bookmark] =
self.backend.getBookmarks()
proc deleteBookmark*(self: BackendWrapper, url: string) =
self.backend.deleteBookmark(url)

View File

@ -0,0 +1,23 @@
import tables, sequtils
import types
import ../../types/[bookmark], json, chronicles
import ../backend_type
var bookmarks_storage_mock = initTable[string, Bookmark]()
method storeBookmark*(self: MockBackend, bookmark: Bookmark): Bookmark =
result = bookmark
bookmarks_storage_mock[bookmark.url] = bookmark
method updateBookmark*(self: MockBackend, originalUrl: string, bookmark: Bookmark) =
bookmarks_storage_mock.del(originalUrl)
bookmarks_storage_mock[bookmark.url] = bookmark
method getBookmarks*(self: MockBackend): seq[Bookmark] =
var bookmarks: seq[Bookmark] = @[]
for b in bookmarks_storage_mock.values:
bookmarks.add(b)
result = bookmarks
method deleteBookmark*(self: MockBackend, url: string) =
bookmarks_storage_mock.del(url)

8
backends/mock/main.nim Normal file
View File

@ -0,0 +1,8 @@
import types
proc newMockBackendInstance*(): MockBackend =
result = MockBackend()
export types
# import bookmark
# export bookmark

View File

@ -0,0 +1,5 @@
import types
proc newMockBackendInstance*(): MockBackend =
result = MockBackend()

3
backends/mock/types.nim Normal file
View File

@ -0,0 +1,3 @@
import ../backend_type
type MockBackend* = ref object of Backend

View File

@ -1,6 +1,6 @@
import json, chronicles
import core
import ../types/[bookmark]
import types
import core, ../../types/[bookmark], json, chronicles
import ../backend_type
proc storeBookmark*(bookmark: Bookmark): Bookmark =
let payload = %* [{"url": bookmark.url, "name": bookmark.name}]

View File

@ -0,0 +1,58 @@
import json, nimcrypto, chronicles
import status_go, ../../status/utils
logScope:
topics = "rpc"
proc callRPC*(inputJSON: string): string =
return $status_go.callRPC(inputJSON)
proc callPrivateRPCRaw*(inputJSON: string): string =
return $status_go.callPrivateRPC(inputJSON)
proc callPrivateRPC*(methodName: string, payload = %* []): string =
try:
let inputJSON = %* {
"jsonrpc": "2.0",
"method": methodName,
"params": %payload
}
debug "callPrivateRPC", rpc_method=methodName
let response = status_go.callPrivateRPC($inputJSON)
result = $response
if parseJSON(result).hasKey("error"):
error "rpc response error", result, payload, methodName
except Exception as e:
error "error doing rpc request", methodName = methodName, exception=e.msg
# proc sendTransaction*(inputJSON: string, password: string): string =
# var hashed_password = "0x" & $keccak_256.digest(password)
# return $status_go.sendTransaction(inputJSON, hashed_password)
# proc startMessenger*() =
# discard callPrivateRPC("startMessenger".prefix)
# proc addPeer*(peer: string) =
# discard callPrivateRPC("admin_addPeer", %* [peer])
# proc removePeer*(peer: string) =
# discard callPrivateRPC("admin_removePeer", %* [peer])
# proc markTrustedPeer*(peer: string) =
# discard callPrivateRPC("markTrustedPeer".prefix(false), %* [peer])
# proc getBlockByNumber*(blockNumber: string): string =
# result = callPrivateRPC("eth_getBlockByNumber", %* [blockNumber, false])
# proc getTransfersByAddress*(address: string, toBlock: string, limit: string, fetchMore: bool = false): string =
# let toBlockParsed = if not fetchMore: newJNull() else: %toBlock
# result = callPrivateRPC("wallet_getTransfersByAddress", %* [address, toBlockParsed, limit, fetchMore])
# proc signMessage*(rpcParams: string): string =
# return $status_go.signMessage(rpcParams)
# proc signTypedData*(data: string, address: string, password: string): string =
# return $status_go.signTypedData(data, address, password)
# proc getBloomFilter*(): string =
# return $callPrivateRPC("bloomFilter".prefix, %* []).parseJSON()["result"].getStr

View File

@ -0,0 +1,8 @@
import types
proc newStatusGoBackendInstance*(): StatusGoBackend =
result = StatusGoBackend()
export types
import bookmark
export bookmark

View File

@ -0,0 +1,4 @@
import types
proc newStatusGoBackendInstance*(): StatusGoBackend =
result = StatusGoBackend()

View File

@ -0,0 +1,3 @@
import ../backend_type
type StatusGoBackend* = ref object of Backend

7
env.sh Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
# We use ${BASH_SOURCE[0]} instead of $0 to allow sourcing this file
# and we fall back to a Zsh-specific special var to also support Zsh.
REL_PATH="$(dirname ${BASH_SOURCE[0]:-${(%):-%x}})"
ABS_PATH="$(cd ${REL_PATH}; pwd)"
source ${ABS_PATH}/vendor/nimbus-build-system/scripts/env.sh

View File

@ -7,3 +7,46 @@ license = "MIT"
skipDirs = @["test"]
requires "nim >= 1.2.0"
import strutils
const release_opts =
" --define:danger" &
" --define:strip" &
" --hints:off" &
" --opt:size" &
" --passC:-flto" &
" --passL:-flto"
const debug_opts =
" --debugger:native" &
" --define:chronicles_line_numbers" &
" --define:debug" &
" --linetrace:on" &
" --stacktrace:on"
proc buildAndRun(name: string,
srcDir = "test_nim/",
outDir = "test_nim/build/",
params = "",
cmdParams = "",
lang = "c") =
mkDir outDir
exec "nim " &
lang &
(if getEnv("RELEASE").strip != "false": release_opts else: debug_opts) &
" --define:ssl" &
" --passL:" & getEnv("STATUSGO") & "" &
# " --passL:" & "vendor/status-go/build/bin/libstatus.dylib" & "" &
" --out:" & outDir & name &
" " &
srcDir & name & ".nim"
if defined(macosx):
exec "install_name_tool -add_rpath " & getEnv("STATUSGO_LIBDIR") & " " & outDir & name
exec "install_name_tool -change " & "libstatus." & getEnv("LIBSTATUS_EXT") & " @rpath/libstatus." & getEnv("LIBSTATUS_EXT") & " " & outDir & name
if getEnv("RUN_AFTER_BUILD").strip != "false":
exec outDir & name
task tests, "Build and run all tests":
rmDir "test_nim/build/"
buildAndRun "test_all"

View File

@ -1,23 +1,28 @@
import statusgo_backend/browser as status_browser
# import statusgo_backend/browser as status_browser
import ../eventemitter
import ./types/[bookmark]
import ../types/[bookmark]
import ../backends/backend
type
BrowserModel* = ref object
events*: EventEmitter
backend*: BackendWrapper
proc newBrowserModel*(events: EventEmitter): BrowserModel =
proc newBrowserModel*(events: EventEmitter, backend: BackendWrapper): BrowserModel =
result = BrowserModel()
result.events = events
result.backend = backend
proc storeBookmark*(self: BrowserModel, bookmark: Bookmark): Bookmark =
return status_browser.storeBookmark(bookmark)
return self.backend.storeBookmark(bookmark)
proc updateBookmark*(self: BrowserModel, originalUrl: string, bookmark: Bookmark) =
status_browser.updateBookmark(originalUrl, bookmark)
self.backend.updateBookmark(originalUrl, bookmark)
proc getBookmarks*(self: BrowserModel): seq[Bookmark] =
result = status_browser.getBookmarks()
result = self.backend.getBookmarks()
proc deleteBookmark*(self: BrowserModel, url: string) =
status_browser.deleteBookmark(url)
self.backend.deleteBookmark(url)

View File

@ -7,9 +7,12 @@ import ../eventemitter
import bitops, stew/byteutils, chronicles
import ./types/[setting]
import ../backends/backend
export chat, accounts, node, messages, contacts, profile, network, permissions, fleet, eventemitter
type Status* = ref object
backend*: BackendWrapper
events*: EventEmitter
fleet*: FleetModel
chat*: ChatModel
@ -30,8 +33,10 @@ type Status* = ref object
provider*: ProviderModel
osnotifications*: OsNotifications
proc newStatusInstance*(fleetConfig: string): Status =
proc newStatusInstance*(fleetConfig: string, backendName:string = "statusgo"): Status =
result = Status()
result.backend = newBackendWrapperInstance()
result.backend.loadBackend(backendName)
result.events = createEventEmitter()
result.fleet = fleet.newFleetModel(fleetConfig)
result.chat = chat.newChatModel(result.events)
@ -48,7 +53,7 @@ proc newStatusInstance*(fleetConfig: string): Status =
result.permissions = permissions.newPermissionsModel(result.events)
result.settings = settings.newSettingsModel(result.events)
result.mailservers = mailservers.newMailserversModel(result.events)
result.browser = browser.newBrowserModel(result.events)
result.browser = browser.newBrowserModel(result.events, result.backend)
result.tokens = tokens.newTokensModel(result.events)
result.provider = provider.newProviderModel(result.events, result.permissions, result.wallet)
result.osnotifications = newOsNotifications(result.events)

View File

@ -221,6 +221,6 @@ proc onAsyncFetchCryptoServices*(self: Wallet2Model, response: string) =
self.events.emit("wallet2_cryptoServicesFetched", CryptoServicesArg(services: responseArray))
proc toggleNetwork*(self: StatusWalletController, network: Network) =
proc toggleNetwork*(self: Wallet2Model, network: Network) =
network.enabled = not network.enabled
status_network.upsertNetwork(network)
status_network.upsertNetwork(network)

49
test_nim/bookmarks.nim Normal file
View File

@ -0,0 +1,49 @@
import # std libs
std/[json, options, os, unittest]
import ../status/status
import ../status/browser
import ../types/[bookmark]
suite "#Bookmarks":
setup:
let fleetConfig = "{\"fleets\":{\"eth.prod\":{\"boot\":{\"boot-01.ac-cn-hongkong-c.eth.prod\":\"enode://6e6554fb3034b211398fcd0f0082cbb6bd13619e1a7e76ba66e1809aaa0c5f1ac53c9ae79cf2fd4a7bacb10d12010899b370c75fed19b991d9c0cdd02891abad@47.75.99.169:443\"},\"mail\":{\"mail-01.ac-cn-hongkong-c.eth.prod\":\"enode://606ae04a71e5db868a722c77a21c8244ae38f1bd6e81687cc6cfe88a3063fa1c245692232f64f45bd5408fed5133eab8ed78049332b04f9c110eac7f71c1b429@47.75.247.214:443\"},\"rendezvous\":{\"boot-01.ac-cn-hongkong-c.eth.prod\":\"/ip4/47.75.99.169/tcp/30703/ethv4/16Uiu2HAmV8Hq9e3zm9TMVP4zrVHo3BjqW5D6bDVV6VQntQd687e4\"},\"whisper\":{\"node-01.ac-cn-hongkong-c.eth.prod\":\"enode://b957e51f41e4abab8382e1ea7229e88c6e18f34672694c6eae389eac22dab8655622bbd4a08192c321416b9becffaab11c8e2b7a5d0813b922aa128b82990dab@47.75.222.178:443\"}}},\"meta\":{\"hostname\":\"node-01.do-ams3.proxy.misc\",\"timestamp\":\"2021-09-09T00:00:14.760436\"}}";
let statuslib_instance = newStatusInstance(fleetConfig, "mock")
test "storeBookmark: should store a bookmark":
let new_bookmark = Bookmark(name: "status", url: "https://status.im")
let returned_bookmark = statuslib_instance.browser.storeBookmark(new_bookmark)
check(returned_bookmark.name == "status")
check(returned_bookmark.url == "https://status.im")
let stored_bookmarks = statuslib_instance.browser.getBookmarks()
check(stored_bookmarks[0].name == "status")
check(stored_bookmarks[0].url == "https://status.im")
test "getBookmarks: should get list of stored bookmarks":
let new_bookmark = Bookmark(name: "status2", url: "https://status2.im")
let returned_bookmark = statuslib_instance.browser.storeBookmark(new_bookmark)
check(returned_bookmark.name == "status2")
check(returned_bookmark.url == "https://status2.im")
let stored_bookmarks = statuslib_instance.browser.getBookmarks()
check(stored_bookmarks.len == 2)
check(stored_bookmarks[0].name == "status")
check(stored_bookmarks[0].url == "https://status.im")
check(stored_bookmarks[1].name == "status2")
check(stored_bookmarks[1].url == "https://status2.im")
test "deleteBookmark: should delete an existing bookmark":
statuslib_instance.browser.deleteBookmark("https://status.im")
let stored_bookmarks = statuslib_instance.browser.getBookmarks()
check(stored_bookmarks.len == 1)
check(stored_bookmarks[0].name == "status2")
check(stored_bookmarks[0].url == "https://status2.im")
test "updateBookmark: should update an existing bookmark":
let new_bookmark = Bookmark(name: "status3", url: "https://status3.im")
statuslib_instance.browser.updateBookmark("https://status2.im", new_bookmark)
let stored_bookmarks = statuslib_instance.browser.getBookmarks()
check(stored_bookmarks.len == 1)
check(stored_bookmarks[0].name == "status3")
check(stored_bookmarks[0].url == "https://status3.im")

2
test_nim/test_all.nim Normal file
View File

@ -0,0 +1,2 @@
import
./bookmarks

View File

@ -1,7 +1,5 @@
{.used.}
type Bookmark* = ref object
name*: string
url*: string
imageUrl*: string