<commit_before>// +build !clustered,!gcloud

package datastore

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"sync"
	"testing"
	"time"

	"github.com/janelia-flyem/dvid/dvid"
	"github.com/janelia-flyem/dvid/storage"
	"github.com/janelia-flyem/dvid/storage/local"
	"github.com/janelia-flyem/go/uuid"
)

func TestRepoGobEncoding(t *testing.T) {
	now := time.Now()
	repo := &repoT{
		repoID: 3,
		rootID: dvid.UUID("23f8"),
		log: []string{
			"Did this",
			"Then that",
			"And the other thing",
		},
		properties: map[string]interface{}{
			"foo": 42,
			"bar": "some string",
			"baz": []int{3, 9, 7},
		},
		dag:     &dagT{},
		data:    make(map[dvid.InstanceName]DataService),
		created: now,
		updated: now,
	}
	encoding, err := repo.GobEncode()
	if err != nil {
		t.Fatalf("Could not encode repo: %s\n", err.Error())
	}
	received := repoT{}
	if err = received.GobDecode(encoding); err != nil {
		t.Fatalf("Could not decode repo: %s\n", err.Error())
	}
	// Test DAG elsewhere
	repo.dag = nil
	received.dag = nil
	if len(received.properties) != 3 {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	foo, ok := received.properties["foo"]
	if !ok || foo != 42 {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	bar, ok := received.properties["bar"]
	if !ok || bar != "some string" {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	baz, ok := received.properties["baz"]
	if !ok || !reflect.DeepEqual(baz, []int{3, 9, 7}) {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	repo.properties = nil
	received.properties = nil
	if !reflect.DeepEqual(*repo, received) {
		t.Fatalf("Repo Gob messed up:\nOriginal: %v\nReceived: %v\n", *repo, received)
	}
}

const (
	WebAddress   = "localhost:8657"
	RPCAddress   = "localhost:8658"
	WebClientDir = ""
)

var (
	engine storage.Engine
	count  int
	dbpath string
	mu     sync.Mutex
)

func useStore() {
	mu.Lock()
	defer mu.Unlock()
	if count == 0 {
		dbpath = filepath.Join(os.TempDir(), fmt.Sprintf("dvid-test-%s", uuid.NewV4()))
		var err error
		engine, err = local.CreateBlankStore(dbpath)
		if err != nil {
			log.Fatalf("Can't create a blank test datastore: %s\n", err.Error())
		}
		if err = storage.Initialize(engine, "testdb"); err != nil {
			log.Fatalf("Can't initialize test datastore: %s\n", err.Error())
		}
		if err = InitMetadata(engine); err != nil {
			log.Fatalf("Can't write blank datastore metadata: %s\n", err.Error())
		}
		if err = Initialize(); err != nil {
			log.Fatalf("Can't initialize datastore management: %s\n", err.Error())
		}
	}
	count++
}

// closeReopenStore forces close of the underlying storage engine and then reopening
// the datastore.  Useful for testing metadata persistence.
func closeReopenStore() {
	mu.Lock()
	defer mu.Unlock()
	dvid.BlockOnActiveCgo()
	if engine == nil {
		log.Fatalf("Attempted to close and reopen non-existant engine!")
	}
	engine.Close()

	var err error
	create := false
	engine, err = local.NewKeyValueStore(dbpath, create, dvid.Config{})
	if err != nil {
		log.Fatalf("Error reopening test db at %s: %s\n", dbpath, err.Error())
	}
	if err = storage.Initialize(engine, "testdb"); err != nil {
		log.Fatalf("Can't initialize test datastore: %s\n", err.Error())
	}
	if err = Initialize(); err != nil {
		log.Fatalf("Can't initialize datastore management: %s\n", err.Error())
	}
}

func closeStore() {
	mu.Lock()
	defer mu.Unlock()
	count--
	if count == 0 {
		dvid.BlockOnActiveCgo()
		if engine == nil {
			log.Fatalf("Attempted to close non-existant engine!")
		}
		// Close engine and delete store.
		engine.Close()
		engine = nil
		if err := os.RemoveAll(dbpath); err != nil {
			log.Fatalf("Unable to cleanup test store: %s\n", dbpath)
		}
	}
}

func TestCommandLine(t *testing.T) {
	useStore()
	defer closeStore()
}

func TestRepoPersistence(t *testing.T) {
	useStore()

	repo, err := NewRepo("test repo", "test repo description", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	if err := repo.Commit(repo.RootUUID(), "root node", nil); err != nil {
		t.Fatal(err.Error())
	}

	child1, err := repo.NewVersion(repo.RootUUID(), nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := repo.Commit(child1, "child 1", nil); err != nil {
		t.Fatal(err.Error())
	}

	child2, err := repo.NewVersion(repo.RootUUID(), nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := repo.Commit(child2, "child 2", nil); err != nil {
		t.Fatal(err.Error())
	}

	// Save this metadata<commit_msg>add node-level log persistence to tests<commit_after>// +build !clustered,!gcloud

package datastore

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"sync"
	"testing"
	"time"

	"github.com/janelia-flyem/dvid/dvid"
	"github.com/janelia-flyem/dvid/storage"
	"github.com/janelia-flyem/dvid/storage/local"
	"github.com/janelia-flyem/go/uuid"
)

func TestRepoGobEncoding(t *testing.T) {
	now := time.Now()
	repo := &repoT{
		repoID: 3,
		rootID: dvid.UUID("23f8"),
		log: []string{
			"Did this",
			"Then that",
			"And the other thing",
		},
		properties: map[string]interface{}{
			"foo": 42,
			"bar": "some string",
			"baz": []int{3, 9, 7},
		},
		dag:     &dagT{},
		data:    make(map[dvid.InstanceName]DataService),
		created: now,
		updated: now,
	}
	encoding, err := repo.GobEncode()
	if err != nil {
		t.Fatalf("Could not encode repo: %s\n", err.Error())
	}
	received := repoT{}
	if err = received.GobDecode(encoding); err != nil {
		t.Fatalf("Could not decode repo: %s\n", err.Error())
	}
	// Test DAG elsewhere
	repo.dag = nil
	received.dag = nil
	if len(received.properties) != 3 {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	foo, ok := received.properties["foo"]
	if !ok || foo != 42 {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	bar, ok := received.properties["bar"]
	if !ok || bar != "some string" {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	baz, ok := received.properties["baz"]
	if !ok || !reflect.DeepEqual(baz, []int{3, 9, 7}) {
		t.Errorf("Repo Gob messed up properties: %v\n", received.properties)
	}
	repo.properties = nil
	received.properties = nil
	if !reflect.DeepEqual(*repo, received) {
		t.Fatalf("Repo Gob messed up:\nOriginal: %v\nReceived: %v\n", *repo, received)
	}
}

const (
	WebAddress   = "localhost:8657"
	RPCAddress   = "localhost:8658"
	WebClientDir = ""
)

var (
	engine storage.Engine
	count  int
	dbpath string
	mu     sync.Mutex
)

func useStore() {
	mu.Lock()
	defer mu.Unlock()
	if count == 0 {
		dbpath = filepath.Join(os.TempDir(), fmt.Sprintf("dvid-test-%s", uuid.NewV4()))
		var err error
		engine, err = local.CreateBlankStore(dbpath)
		if err != nil {
			log.Fatalf("Can't create a blank test datastore: %s\n", err.Error())
		}
		if err = storage.Initialize(engine, "testdb"); err != nil {
			log.Fatalf("Can't initialize test datastore: %s\n", err.Error())
		}
		if err = InitMetadata(engine); err != nil {
			log.Fatalf("Can't write blank datastore metadata: %s\n", err.Error())
		}
		if err = Initialize(); err != nil {
			log.Fatalf("Can't initialize datastore management: %s\n", err.Error())
		}
	}
	count++
}

// closeReopenStore forces close of the underlying storage engine and then reopening
// the datastore.  Useful for testing metadata persistence.
func closeReopenStore() {
	mu.Lock()
	defer mu.Unlock()
	dvid.BlockOnActiveCgo()
	if engine == nil {
		log.Fatalf("Attempted to close and reopen non-existant engine!")
	}
	engine.Close()

	var err error
	create := false
	engine, err = local.NewKeyValueStore(dbpath, create, dvid.Config{})
	if err != nil {
		log.Fatalf("Error reopening test db at %s: %s\n", dbpath, err.Error())
	}
	if err = storage.Initialize(engine, "testdb"); err != nil {
		log.Fatalf("Can't initialize test datastore: %s\n", err.Error())
	}
	if err = Initialize(); err != nil {
		log.Fatalf("Can't initialize datastore management: %s\n", err.Error())
	}
}

func closeStore() {
	mu.Lock()
	defer mu.Unlock()
	count--
	if count == 0 {
		dvid.BlockOnActiveCgo()
		if engine == nil {
			log.Fatalf("Attempted to close non-existant engine!")
		}
		// Close engine and delete store.
		engine.Close()
		engine = nil
		if err := os.RemoveAll(dbpath); err != nil {
			log.Fatalf("Unable to cleanup test store: %s\n", dbpath)
		}
	}
}

func TestCommandLine(t *testing.T) {
	useStore()
	defer closeStore()
}

func TestRepoPersistence(t *testing.T) {
	useStore()

	repo, err := NewRepo("test repo", "test repo description", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	if err := repo.Commit(repo.RootUUID(), "root node", nil); err != nil {
		t.Fatal(err.Error())
	}

	child1, err := repo.NewVersion(repo.RootUUID(), nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	if err := repo.Commit(child1, "child 1", nil); err != nil {
		t.Fatal(err.Error())
	}

	child2, err := repo.NewVersion(repo.RootUUID(), nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	log2 := []string{"This is line 1 of log", "This is line 2 of log", "Last line for multiline log"}
	if err := repo.Commit(child2, "child 2", log2); err != nil {
		t.Fatal(err.Error())
	}

	child3, err := repo.NewVersion(repo.RootUUID(), nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	nodelog := []string{`My first node-level log line.!(;#)}`, "Second line is here!!!"}
	if err := repo.AddToNodeLog(child3, nodelog); err != nil {
		t.Fatalf(err.Error())
	}
	// Don't save this child.

	// Save this metadata
{'fraction_non_alphanumeric': 0.17227513227513228, 'fraction_numerical': 0.007407407407407408, 'mean_word_length': 3.6973161033797215, 'pattern_counts': {'":': 6, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1977902', 'n_tokens_mistral': 3750, 'n_tokens_neox': 3539, 'n_words': 903}