mirror of
https://github.com/logos-storage/logos-storage-go-bindings.git
synced 2026-01-07 16:03:06 +00:00
Add first features
This commit is contained in:
parent
4c421675dc
commit
7e88bddce1
159
codex/bridge.go
Normal file
159
codex/bridge.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package codex
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -L../vendor/nim-codex/build/ -lcodex
|
||||||
|
#cgo LDFLAGS: -L../vendor/nim-codex/ -Wl,-rpath,../vendor/nim-codex/build
|
||||||
|
|
||||||
|
#include "../vendor/nim-codex/library/libcodex.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int ret;
|
||||||
|
char* msg;
|
||||||
|
size_t len;
|
||||||
|
uintptr_t h;
|
||||||
|
} Resp;
|
||||||
|
|
||||||
|
static void* allocResp(uintptr_t h) {
|
||||||
|
Resp* r = (Resp*)calloc(1, sizeof(Resp));
|
||||||
|
r->h = h;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeResp(void* resp) {
|
||||||
|
if (resp != NULL) {
|
||||||
|
free(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getRet(void* resp) {
|
||||||
|
if (resp == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Resp* m = (Resp*) resp;
|
||||||
|
return m->ret;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"runtime/cgo"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// bridgeCtx is used for managing the C-Go bridge calls.
|
||||||
|
// It contains a wait group for synchronizing the calls,
|
||||||
|
// a cgo.Handle for passing context to the C code,
|
||||||
|
// a response pointer for receiving data from the C code,
|
||||||
|
// and fields for storing the result and error of the call.
|
||||||
|
type bridgeCtx struct {
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
h cgo.Handle
|
||||||
|
resp unsafe.Pointer
|
||||||
|
result string
|
||||||
|
err error
|
||||||
|
|
||||||
|
// Callback used for receiving progress updates during upload/download.
|
||||||
|
//
|
||||||
|
// For the upload, the bytes parameter indicates the number of bytes uploaded.
|
||||||
|
// If the chunk size is superior or equal to the blocksize (passed in init function),
|
||||||
|
// the callback will be called when a block is put in the store.
|
||||||
|
// Otherwise, it will be called when a chunk is pushed into the stream.
|
||||||
|
//
|
||||||
|
// For the download, the bytes is the size of the chunk received, and the chunk
|
||||||
|
// is the actual chunk of data received.
|
||||||
|
onProgress func(bytes int, chunk []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBridgeCtx creates a new bridge context for managing C-Go calls.
|
||||||
|
// The bridge context is initialized with a wait group and a cgo.Handle.
|
||||||
|
func newBridgeCtx() *bridgeCtx {
|
||||||
|
bridge := &bridgeCtx{}
|
||||||
|
bridge.wg = &sync.WaitGroup{}
|
||||||
|
bridge.wg.Add(1)
|
||||||
|
bridge.h = cgo.NewHandle(bridge)
|
||||||
|
bridge.resp = C.allocResp(C.uintptr_t(uintptr(bridge.h)))
|
||||||
|
return bridge
|
||||||
|
}
|
||||||
|
|
||||||
|
// callError creates an error message for a failed C-Go call.
|
||||||
|
func (b *bridgeCtx) callError(name string) error {
|
||||||
|
return fmt.Errorf("failed the call to %s returned code %d", name, C.getRet(b.resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
// free releases the resources associated with the bridge context,
|
||||||
|
// including the cgo.Handle and the response pointer.
|
||||||
|
func (b *bridgeCtx) free() {
|
||||||
|
if b.h > 0 {
|
||||||
|
b.h.Delete()
|
||||||
|
b.h = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.resp != nil {
|
||||||
|
C.freeResp(b.resp)
|
||||||
|
b.resp = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback is the function called by the C code to communicate back to Go.
|
||||||
|
// It handles progress updates, successful completions, and errors.
|
||||||
|
// The function uses the response pointer to retrieve the bridge context
|
||||||
|
// and update its state accordingly.
|
||||||
|
//
|
||||||
|
//export callback
|
||||||
|
func callback(ret C.int, msg *C.char, len C.size_t, resp unsafe.Pointer) {
|
||||||
|
if resp == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := (*C.Resp)(resp)
|
||||||
|
m.ret = ret
|
||||||
|
m.msg = msg
|
||||||
|
m.len = len
|
||||||
|
|
||||||
|
if m.h == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h := cgo.Handle(m.h)
|
||||||
|
if h == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := h.Value().(*bridgeCtx); ok {
|
||||||
|
switch ret {
|
||||||
|
case C.RET_PROGRESS:
|
||||||
|
if v.onProgress == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg != nil {
|
||||||
|
chunk := C.GoBytes(unsafe.Pointer(msg), C.int(len))
|
||||||
|
v.onProgress(int(C.int(len)), chunk)
|
||||||
|
} else {
|
||||||
|
v.onProgress(int(C.int(len)), nil)
|
||||||
|
}
|
||||||
|
case C.RET_OK:
|
||||||
|
retMsg := C.GoStringN(msg, C.int(len))
|
||||||
|
v.result = retMsg
|
||||||
|
v.err = nil
|
||||||
|
if v.wg != nil {
|
||||||
|
v.wg.Done()
|
||||||
|
}
|
||||||
|
case C.RET_ERR:
|
||||||
|
retMsg := C.GoStringN(msg, C.int(len))
|
||||||
|
v.err = errors.New(retMsg)
|
||||||
|
if v.wg != nil {
|
||||||
|
v.wg.Done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait waits for the bridge context to complete its operation.
|
||||||
|
// It returns the result and error of the operation.
|
||||||
|
func (b *bridgeCtx) wait() (string, error) {
|
||||||
|
b.wg.Wait()
|
||||||
|
return b.result, b.err
|
||||||
|
}
|
||||||
5
codex/bridge.h
Normal file
5
codex/bridge.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../vendor/nim-codex/library/libcodex.h"
|
||||||
|
|
||||||
|
extern void callback(int ret, char* msg, size_t len, void* resp);
|
||||||
224
codex/codex.go
Normal file
224
codex/codex.go
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
package codex
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -L../vendor/nim-codex/build/ -lcodex
|
||||||
|
#cgo LDFLAGS: -L../vendor/nim-codex/ -Wl,-rpath,../vendor/nim-codex/
|
||||||
|
|
||||||
|
#include "bridge.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void libcodexNimMain(void);
|
||||||
|
|
||||||
|
static void codex_host_init_once(void){
|
||||||
|
static int done;
|
||||||
|
if (!__atomic_exchange_n(&done, 1, __ATOMIC_SEQ_CST)) libcodexNimMain();
|
||||||
|
}
|
||||||
|
|
||||||
|
// resp must be set != NULL in case interest on retrieving data from the callback
|
||||||
|
void callback(int ret, char* msg, size_t len, void* resp);
|
||||||
|
|
||||||
|
static void* cGoCodexNew(const char* configJson, void* resp) {
|
||||||
|
void* ret = codex_new(configJson, (CodexCallback) callback, resp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cGoCodexStart(void* codexCtx, void* resp) {
|
||||||
|
return codex_start(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cGoCodexStop(void* codexCtx, void* resp) {
|
||||||
|
return codex_stop(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cGoCodexDestroy(void* codexCtx, void* resp) {
|
||||||
|
return codex_destroy(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cGoCodexVersion(void* codexCtx, void* resp) {
|
||||||
|
return codex_version(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cGoCodexRevision(void* codexCtx, void* resp) {
|
||||||
|
return codex_revision(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cGoCodexRepo(void* codexCtx, void* resp) {
|
||||||
|
return codex_repo(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cGoCodexSpr(void* codexCtx, void* resp) {
|
||||||
|
return codex_spr(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogLevel string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Trace LogLevel = "TRACE"
|
||||||
|
Debug LogLevel = "DEBUG"
|
||||||
|
Info LogLevel = "INFO"
|
||||||
|
Notice LogLevel = "NOTICE"
|
||||||
|
Warn LogLevel = "WARN"
|
||||||
|
Error LogLevel = "ERROR"
|
||||||
|
Fatal LogLevel = "FATAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogFormat string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LogFormatAuto LogFormat = "auto"
|
||||||
|
LogFormatColors LogFormat = "colors"
|
||||||
|
LogFormatNoColors LogFormat = "nocolors"
|
||||||
|
LogFormatJSON LogFormat = "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepoKind string
|
||||||
|
|
||||||
|
const (
|
||||||
|
FS RepoKind = "fs"
|
||||||
|
SQLite RepoKind = "sqlite"
|
||||||
|
LevelDb RepoKind = "leveldb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodexConfig struct {
|
||||||
|
LogFormat LogFormat `json:"log-format,omitempty"`
|
||||||
|
MetricsEnabled bool `json:"metrics,omitempty"`
|
||||||
|
MetricsAddress string `json:"metrics-address,omitempty"`
|
||||||
|
DataDir string `json:"data-dir,omitempty"`
|
||||||
|
ListenAddrs []string `json:"listen-addrs,omitempty"`
|
||||||
|
Nat string `json:"nat,omitempty"`
|
||||||
|
DiscoveryPort int `json:"disc-port,omitempty"`
|
||||||
|
NetPrivKeyFile string `json:"net-privkey,omitempty"`
|
||||||
|
BootstrapNodes []byte `json:"bootstrap-node,omitempty"`
|
||||||
|
MaxPeers int `json:"max-peers,omitempty"`
|
||||||
|
NumThreads int `json:"num-threads,omitempty"`
|
||||||
|
AgentString string `json:"agent-string,omitempty"`
|
||||||
|
RepoKind RepoKind `json:"repo-kind,omitempty"`
|
||||||
|
StorageQuota int `json:"storage-quota,omitempty"`
|
||||||
|
BlockTtl int `json:"block-ttl,omitempty"`
|
||||||
|
BlockMaintenanceInterval int `json:"block-mi,omitempty"`
|
||||||
|
BlockMaintenanceNumberOfBlocks int `json:"block-mn,omitempty"`
|
||||||
|
CacheSize int `json:"cache-size,omitempty"`
|
||||||
|
LogFile string `json:"log-file,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodexNode struct {
|
||||||
|
ctx unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodexNew creates a new Codex node with the provided configuration.
|
||||||
|
// The node is not started automatically; you need to call CodexStart
|
||||||
|
// to start it.
|
||||||
|
// It returns a Codex node that can be used to interact
|
||||||
|
// with the Codex network.
|
||||||
|
func CodexNew(config CodexConfig) (*CodexNode, error) {
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
defer bridge.free()
|
||||||
|
|
||||||
|
jsonConfig, err := json.Marshal(config)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cJsonConfig := C.CString(string(jsonConfig))
|
||||||
|
defer C.free(unsafe.Pointer(cJsonConfig))
|
||||||
|
|
||||||
|
ctx := C.cGoCodexNew(cJsonConfig, bridge.resp)
|
||||||
|
|
||||||
|
if _, err := bridge.wait(); err != nil {
|
||||||
|
return nil, bridge.err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CodexNode{ctx: ctx}, bridge.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the Codex node.
|
||||||
|
// TODO waits for the node to be fully started,
|
||||||
|
// by looking into the logs.
|
||||||
|
func (node CodexNode) Start() error {
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
defer bridge.free()
|
||||||
|
|
||||||
|
if C.cGoCodexStart(node.ctx, bridge.resp) != C.RET_OK {
|
||||||
|
return bridge.callError("cGoCodexStart")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := bridge.wait()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartAsync is the asynchronous version of Start.
|
||||||
|
func (node CodexNode) StartAsync(onDone func(error)) {
|
||||||
|
go func() {
|
||||||
|
err := node.Start()
|
||||||
|
onDone(err)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the Codex node.
|
||||||
|
func (node CodexNode) Stop() error {
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
|
||||||
|
if C.cGoCodexStop(node.ctx, bridge.resp) != C.RET_OK {
|
||||||
|
return bridge.callError("cGoCodexStop")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := bridge.wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy destroys the Codex node, freeing all resources.
|
||||||
|
// The node must be stopped before calling this method.
|
||||||
|
func (node CodexNode) Destroy() error {
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
|
||||||
|
if C.cGoCodexDestroy(node.ctx, bridge.resp) != C.RET_OK {
|
||||||
|
return bridge.callError("cGoCodexDestroy")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := bridge.wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version returns the version of the Codex node.
|
||||||
|
func (node CodexNode) Version() (string, error) {
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
defer bridge.free()
|
||||||
|
|
||||||
|
if C.cGoCodexVersion(node.ctx, bridge.resp) != C.RET_OK {
|
||||||
|
return "", bridge.callError("cGoCodexVersion")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revision returns the revision of the Codex node.
|
||||||
|
func (node CodexNode) Revision() (string, error) {
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
defer bridge.free()
|
||||||
|
|
||||||
|
if C.cGoCodexRevision(node.ctx, bridge.resp) != C.RET_OK {
|
||||||
|
return "", bridge.callError("cGoCodexRevision")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repo returns the path of the data dir folder.
|
||||||
|
func (node CodexNode) Repo() (string, error) {
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
defer bridge.free()
|
||||||
|
|
||||||
|
if C.cGoCodexRepo(node.ctx, bridge.resp) != C.RET_OK {
|
||||||
|
return "", bridge.callError("cGoCodexRepo")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge.wait()
|
||||||
|
}
|
||||||
59
codex/codex_test.go
Normal file
59
codex/codex_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package codex
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCodexVersion(t *testing.T) {
|
||||||
|
node, err := CodexNew(CodexConfig{
|
||||||
|
DataDir: t.TempDir(),
|
||||||
|
LogFormat: LogFormatNoColors,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create Codex node: %v", err)
|
||||||
|
}
|
||||||
|
defer node.Destroy()
|
||||||
|
|
||||||
|
version, err := node.Version()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get Codex version: %v", err)
|
||||||
|
}
|
||||||
|
if version == "" {
|
||||||
|
t.Fatal("Codex version is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Codex version: %s", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCodexRevision(t *testing.T) {
|
||||||
|
node, err := CodexNew(CodexConfig{
|
||||||
|
DataDir: t.TempDir(),
|
||||||
|
LogFormat: LogFormatNoColors,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create Codex node: %v", err)
|
||||||
|
}
|
||||||
|
defer node.Destroy()
|
||||||
|
|
||||||
|
revision, err := node.Revision()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get Codex revision: %v", err)
|
||||||
|
}
|
||||||
|
if revision == "" {
|
||||||
|
t.Fatal("Codex revision is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Codex revision: %s", revision)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCodexRepo(t *testing.T) {
|
||||||
|
node := newCodexNode(t)
|
||||||
|
|
||||||
|
repo, err := node.Repo()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get Codex repo: %v", err)
|
||||||
|
}
|
||||||
|
if repo == "" {
|
||||||
|
t.Fatal("Codex repo is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Codex repo: %s", repo)
|
||||||
|
}
|
||||||
52
codex/debug.go
Normal file
52
codex/debug.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package codex
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "bridge.h"
|
||||||
|
|
||||||
|
static int cGoCodexDebug(void* codexCtx, void* resp) {
|
||||||
|
return codex_debug(codexCtx, (CodexCallback) callback, resp);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
NodeId string `json:"nodeId"`
|
||||||
|
PeerId string `json:"peerId"`
|
||||||
|
Record string `json:"record"`
|
||||||
|
Address *string `json:"address"`
|
||||||
|
Seen bool `json:"seen"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoutingTable struct {
|
||||||
|
LocalNode Node `json:"localNode"`
|
||||||
|
Nodes []Node `json:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DebugInfo struct {
|
||||||
|
ID string `json:"id"` // Peer ID
|
||||||
|
Addrs []string `json:"addrs"` // Peer info addresses
|
||||||
|
Spr string `json:"spr"` // Signed Peer Record
|
||||||
|
AnnounceAddresses []string `json:"announceAddresses"`
|
||||||
|
PeersTable RoutingTable `json:"table"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug retrieves debugging information from the Codex node.
|
||||||
|
func (node CodexNode) Debug() (DebugInfo, error) {
|
||||||
|
var info DebugInfo
|
||||||
|
|
||||||
|
bridge := newBridgeCtx()
|
||||||
|
defer bridge.free()
|
||||||
|
|
||||||
|
if C.cGoCodexDebug(node.ctx, bridge.resp) != C.RET_OK {
|
||||||
|
return info, bridge.callError("cGoCodexDebug")
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := bridge.wait()
|
||||||
|
if err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(value), &info)
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
23
codex/debug_test.go
Normal file
23
codex/debug_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package codex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDebug(t *testing.T) {
|
||||||
|
codex := newCodexNode(t)
|
||||||
|
|
||||||
|
info, err := codex.Debug()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Debug call failed: %v", err)
|
||||||
|
}
|
||||||
|
if info.ID == "" {
|
||||||
|
t.Error("Debug info ID is empty")
|
||||||
|
}
|
||||||
|
if info.Spr == "" {
|
||||||
|
t.Error("Debug info Spr is empty")
|
||||||
|
}
|
||||||
|
if len(info.AnnounceAddresses) == 0 {
|
||||||
|
t.Error("Debug info AnnounceAddresses is empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
30
codex/testutil.go
Normal file
30
codex/testutil.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package codex
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func newCodexNode(t *testing.T) *CodexNode {
|
||||||
|
node, err := CodexNew(CodexConfig{
|
||||||
|
DataDir: t.TempDir(),
|
||||||
|
LogFormat: LogFormatNoColors,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create Codex node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = node.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to start Codex node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err := node.Stop(); err != nil {
|
||||||
|
t.Logf("cleanup codex: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := node.Destroy(); err != nil {
|
||||||
|
t.Logf("cleanup codex: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user