Commit a69d2e23 authored by wozz's avatar wozz

refactor/add tests

parent c2c4958f
package main
import (
"log"
"strings"
"time"
"encoding/json"
)
type cache_resp struct {
CacheOK bool
CacheVal string
}
type cache_req struct {
stcmd Stratum_command_msg
retChan chan cache_resp
}
type cache_load struct {
StCmdMsg Stratum_command_msg
Resp string
}
type unspent_cache struct {
Data string
ExpTime time.Time
}
func GetCacheKey(i interface{}) (string, bool) {
switch p := i.(type) {
case []interface{}:
if len(p) == 1 {
switch q := p[0].(type) {
case string:
return q, true
}
}
}
return "", false
}
func CacheManager(ch *CommHub, exp time.Duration) {
TransactionCache := make(map[interface{}]string)
UnspentCache := make(map[interface{}]unspent_cache)
NumBlocks := 0
for {
select {
case cr := <-ch.CacheReq:
resp := cache_resp{false, ""}
scm := cr.stcmd
key, keyok := GetCacheKey(scm.Params)
switch scm.Method {
case "blockchain.transaction.get":
if keyok {
cached_data, ok := TransactionCache[key]
if ok {
log.Println("Data in Cache:", strings.TrimSpace(cached_data))
resp = cache_resp{true, cached_data}
}
}
case "blockchain.address.listunspent":
if keyok {
cached_data, ok := UnspentCache[key]
if ok {
log.Println("Data in Cache:", strings.TrimSpace(cached_data.Data))
if cached_data.ExpTime.Before(time.Now()) {
log.Println("Data in Cache Expired:", time.Now().Sub(cached_data.ExpTime).Seconds())
} else {
resp = cache_resp{true, cached_data.Data}
}
}
}
case "blockchain.numblocks.subscribe":
sr := Stratum_command_resp{
Id: 1,
Result: NumBlocks,
}
msg, err := json.Marshal(sr)
if err != nil {
log.Fatal("json error numblock subscription", err)
}
resp = cache_resp{true, string(msg)}
}
cr.retChan <-resp
case cl := <-ch.CacheLoad:
key, keyok := GetCacheKey(cl.StCmdMsg.Params)
switch cl.StCmdMsg.Method {
case "blockchain.transaction.get":
if keyok {
TransactionCache[key] = cl.Resp
}
case "blockchain.address.listunspent":
if keyok {
exptime := time.Now().Add(exp)
unsp_data := unspent_cache{cl.Resp, exptime}
UnspentCache[key] = unsp_data
}
case "blockchain.numblocks.subscribe":
switch p := cl.StCmdMsg.Params.(type) {
case []interface{}:
switch q := p[0].(type) {
case int:
NumBlocks = q
}
}
}
}
}
}
func checkCache(stcmd Stratum_command_msg, ch *CommHub) cache_resp {
switch stcmd.Method {
case "blockchain.transaction.get", "blockchain.address.listunspent", "blockchain.numblocks.subscribe":
log.Println("Checking Cache for Method:", stcmd.Method)
cacheRespChan := make(chan cache_resp)
ch.CacheReq <-cache_req{stcmd, cacheRespChan}
CacheResp := <-cacheRespChan
return CacheResp
default:
log.Println("Cache not enabled for method:", stcmd.Method)
return cache_resp{false, ""}
}
}
func loadCache(stcmd Stratum_command_msg, resp string, ch *CommHub) {
switch stcmd.Method {
case "blockchain.transaction.get", "blockchain.address.listunspent", "blockchain.numblocks.subscribe":
log.Println("Add to Cache", strings.TrimSpace(resp))
ch.CacheLoad <- cache_load{stcmd, resp}
}
}
package main
import (
"testing"
"encoding/json"
"time"
)
func TestGetCacheKey(t *testing.T) {
input := "{\"params\":[\"key\"]}"
var scm Stratum_command_msg
err := json.Unmarshal([]byte(input), &scm)
if err != nil {
t.Error("Input could not be parsed for test")
}
Key, Valid := GetCacheKey(scm.Params)
if !Valid {
t.Error("Cache Key was Not Valid")
}
if Key != "key" {
t.Error("Cache Key was Not Parsed Correctly")
}
}
func TestGetCacheKeyTooManyParams(t *testing.T) {
input := "{\"params\":[\"key1\", \"key2\"]}"
var scm Stratum_command_msg
err := json.Unmarshal([]byte(input), &scm)
if err != nil {
t.Error("Input could not be parsed for test")
}
Key, Valid := GetCacheKey(scm.Params)
if Valid {
t.Error("Cache Key was Valid when it should not be")
}
if Key != "" {
t.Error("Cache Key was not empty when it should be")
}
}
func TestGetCacheKeyParamsNotList(t *testing.T) {
input := "{\"params\":\"key\"}"
var scm Stratum_command_msg
err := json.Unmarshal([]byte(input), &scm)
if err != nil {
t.Error("Input could not be parsed for test")
}
Key, Valid := GetCacheKey(scm.Params)
if Valid {
t.Error("Cache Key was Valid when it should not be")
}
if Key != "" {
t.Error("Cache Key was not empty when it should be")
}
}
func TestLoadCache(t *testing.T) {
var ch CommHub
ch.CacheLoad = make(chan cache_load, 1)
done := make(chan string)
scm := Stratum_command_msg{}
scm.Method = "blockchain.transaction.get"
loadCache(scm, "pass", &ch)
go func() {
cl := <-ch.CacheLoad
done <- cl.Resp
}()
go func() {
time.Sleep(time.Second)
done <- "timeout"
}()
result := <-done
if result != "pass" {
t.Error("Cache Load was not triggered")
}
}
func TestLoadCacheNonCachedMethod(t *testing.T) {
var ch CommHub
ch.CacheLoad = make(chan cache_load, 1)
done := make(chan string)
scm := Stratum_command_msg{}
scm.Method = "unknown.method"
loadCache(scm, "fail", &ch)
go func() {
cl := <-ch.CacheLoad
done <- cl.Resp
}()
go func() {
time.Sleep(time.Second)
done <- "pass"
}()
result := <-done
if result != "pass" {
t.Error("Cache Load was not triggered")
}
}
func TestCheckCache(t *testing.T) {
var ch CommHub
ch.CacheReq = make(chan cache_req)
scm := Stratum_command_msg{}
scm.Method = "blockchain.transaction.get"
go func() {
cReq := <-ch.CacheReq
cResp := cache_resp{true, cReq.stcmd.Method}
cReq.retChan <-cResp
}()
result := checkCache(scm, &ch)
if !result.CacheOK {
t.Error("Cache result failure")
}
if result.CacheVal != "blockchain.transaction.get" {
t.Error("Cache Value failure")
}
}
func TestCheckCacheNonCachedMethod(t *testing.T) {
var ch CommHub
ch.CacheReq = make(chan cache_req)
scm := Stratum_command_msg{}
scm.Method = "unknown.method"
result := checkCache(scm, &ch)
if result.CacheOK {
t.Error("Cache result does not match expected")
}
if result.CacheVal != "" {
t.Error("Cache value does not match expected result")
}
}
......@@ -10,7 +10,6 @@ import (
"time"
)
type incoming_conn struct {
Conn net.Conn
MsgIds map[int]Stratum_command_msg
......@@ -18,21 +17,6 @@ type incoming_conn struct {
Subscriptions map[string]struct{}
}
type cache_resp struct {
CacheOK bool
CacheVal string
}
type cache_req struct {
stcmd Stratum_command_msg
retChan chan cache_resp
}
type cache_load struct {
StCmdMsg Stratum_command_msg
Resp string
}
type server_out struct {
strat_cmd Stratum_command_msg
ping bool
......@@ -48,114 +32,7 @@ type CommHub struct {
ServerOut chan server_out
}
type unspent_cache struct {
Data string
ExpTime int
}
func GetCacheKey(i interface{}) (string, bool) {
switch p := i.(type) {
case []interface{}:
if len(p) == 1 {
switch q := p[0].(type) {
case string:
return q, true
}
}
}
return "", false
}
func CacheManager(ch *CommHub) {
TransactionCache := make(map[interface{}]string)
UnspentCache := make(map[interface{}]unspent_cache)
NumBlocks := 0
for {
select {
case cr := <-ch.CacheReq:
resp := cache_resp{false, ""}
scm := cr.stcmd
key, keyok := GetCacheKey(scm.Params)
switch scm.Method {
case "blockchain.transaction.get":
if keyok {
cached_data, ok := TransactionCache[key]
if ok {
log.Println("Data in Cache:", strings.TrimSpace(cached_data))
resp = cache_resp{true, cached_data}
}
}
case "blockchain.address.listunspent":
if keyok {
cached_data, ok := UnspentCache[key]
if ok {
log.Println("Data in Cache:", strings.TrimSpace(cached_data.Data))
if cached_data.ExpTime < int(time.Now().Unix()) {
log.Println("Data in Cache Expired:", int(time.Now().Unix()) - cached_data.ExpTime)
} else {
resp = cache_resp{true, cached_data.Data}
}
}
}
case "blockchain.numblocks.subscribe":
sr := Stratum_command_resp{
Id: 1,
Result: NumBlocks,
}
msg, err := json.Marshal(sr)
if err != nil {
log.Fatal("json error numblock subscription", err)
}
resp = cache_resp{true, string(msg)}
}
cr.retChan <-resp
case cl := <-ch.CacheLoad:
key, keyok := GetCacheKey(cl.StCmdMsg.Params)
switch cl.StCmdMsg.Method {
case "blockchain.transaction.get":
if keyok {
TransactionCache[key] = cl.Resp
}
case "blockchain.address.listunspent":
if keyok {
exptime := int(time.Now().Unix()) + 5*60 //5 min exp time
unsp_data := unspent_cache{cl.Resp, exptime}
UnspentCache[key] = unsp_data
}
case "blockchain.numblocks.subscribe":
switch p := cl.StCmdMsg.Params.(type) {
case []interface{}:
switch q := p[0].(type) {
case int:
NumBlocks = q
}
}
}
}
}
}
func checkCache(stcmd Stratum_command_msg, ch *CommHub) cache_resp {
switch stcmd.Method {
case "blockchain.transaction.get", "blockchain.address.listunspent", "blockchain.numblocks.subscribe":
log.Println("Checking Cache for Method:", stcmd.Method)
cacheRespChan := make(chan cache_resp)
ch.CacheReq <-cache_req{stcmd, cacheRespChan}
CacheResp := <-cacheRespChan
return CacheResp
default:
log.Println("Cache not enabled for method:", stcmd.Method)
return cache_resp{false, ""}
}
}
func loadCache(stcmd Stratum_command_msg, resp string, ch *CommHub) {
switch stcmd.Method {
case "blockchain.transaction.get", "blockchain.address.listunspent", "blockchain.numblocks.subscribe":
log.Println("Add to Cache", strings.TrimSpace(resp))
ch.CacheLoad <- cache_load{stcmd, resp}
}
}
func serverRespHandler(ch *CommHub) {
for {
......@@ -322,7 +199,11 @@ func main () {
log.Fatal("error listening", err)
}
defer ln.Close()
go CacheManager(&commhub)
cacheExpTime, err := time.ParseDuration("5m")
if err != nil {
log.Fatal("Cache Exp Time Parse Failure")
}
go CacheManager(&commhub, cacheExpTime)
go outgoingConn(outConn, &commhub)
go outgoingConnResp(outConn, &commhub)
go serverRespHandler(&commhub)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment