From f12c6e30394de4451cdc2dacb67f3e6a10217640 Mon Sep 17 00:00:00 2001 From: lczaplinski Date: Sat, 18 Feb 2017 18:20:36 +0100 Subject: [PATCH] Persistence tests with Ginkgo --- archai_suite_test.go | 13 ++ config.go | 13 +- main.go | 2 +- persistence/migration_session.go | 17 ++- persistence/provider.go | 6 +- persistence_test.go | 221 +++++++++++++++++++++++++++++++ version.go | 2 + 7 files changed, 258 insertions(+), 16 deletions(-) create mode 100644 archai_suite_test.go create mode 100644 persistence_test.go diff --git a/archai_suite_test.go b/archai_suite_test.go new file mode 100644 index 0000000..590f0c0 --- /dev/null +++ b/archai_suite_test.go @@ -0,0 +1,13 @@ +package main_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestArchai(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Archai Suite") +} diff --git a/config.go b/config.go index 415d74c..d60c31c 100644 --- a/config.go +++ b/config.go @@ -5,10 +5,11 @@ import ( "github.com/scoiatael/archai/persistence" ) +// Config is a context for all application actions. type Config struct { - keyspace string - hosts []string - actions []actions.Action + Keyspace string + Hosts []string + Actions []actions.Action } func (c Config) Migrations() map[string]persistence.Migration { @@ -18,13 +19,11 @@ func (c Config) Migrations() map[string]persistence.Migration { } func (c Config) Persistence() persistence.Provider { - hosts := make([]string, 1) - hosts[0] = "127.0.0.1" - provider := persistence.CassandraProvider{Hosts: hosts, - Keyspace: c.keyspace} + provider := persistence.CassandraProvider{Hosts: c.Hosts, Keyspace: c.Keyspace} return &provider } +// Version returns current version func (c Config) Version() string { return Version } diff --git a/main.go b/main.go index 33a4212..fedbb01 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ func main() { app.Usage = "eventstore replacement" app.Version = Version app.Action = func(c *cli.Context) error { - config := Config{keyspace: "archai_test"} + config := Config{Keyspace: "archai_test", Hosts: []string{"127.0.0.1"}} action := actions.ReadEventsToStream{Stream: "testing-stream", Output: os.Stdout} err := action.Run(config) return err diff --git a/persistence/migration_session.go b/persistence/migration_session.go index 8dd1515..baeadbc 100644 --- a/persistence/migration_session.go +++ b/persistence/migration_session.go @@ -3,8 +3,6 @@ package persistence import ( "fmt" - "log" - "github.com/gocql/gocql" "github.com/pkg/errors" ) @@ -37,11 +35,17 @@ var findMigration = fmt.Sprintf(`SELECT name FROM %s WHERE name = ? LIMIT 1`, mi var insertMigration = fmt.Sprintf(`INSERT INTO %s (name) VALUES (?)`, migrationTable) -func (sess *CassandraMigrationSession) ShouldRunMigration(name string) (bool, error) { +func (sess *CassandraMigrationSession) createMigrationTableIfNeeded() error { if err := sess.Query(createMigrationTable).Exec(); err != nil { - return false, errors.Wrap(err, "Query to createMigrationTable failed") + return errors.Wrap(err, "Query to createMigrationTable failed") + } + return nil +} + +func (sess *CassandraMigrationSession) ShouldRunMigration(name string) (bool, error) { + if err := sess.createMigrationTableIfNeeded(); err != nil { + return false, errors.Wrap(err, "Failed to create migrations table if needed") } - log.Println("Looking for migration ", name) iter := sess.Query(findMigration, name).Iter() found := iter.Scan(nil) err := iter.Close() @@ -49,5 +53,8 @@ func (sess *CassandraMigrationSession) ShouldRunMigration(name string) (bool, er } func (sess *CassandraMigrationSession) DidRunMigration(name string) error { + if err := sess.createMigrationTableIfNeeded(); err != nil { + return err + } return sess.Query(insertMigration, name).Exec() } diff --git a/persistence/provider.go b/persistence/provider.go index 7c1ea0c..dd301ce 100644 --- a/persistence/provider.go +++ b/persistence/provider.go @@ -17,12 +17,12 @@ type CassandraProvider struct { Keyspace string } -func (cp *CassandraProvider) newCluster() *gocql.ClusterConfig { +func (cp *CassandraProvider) NewCluster() *gocql.ClusterConfig { return gocql.NewCluster(cp.Hosts...) } func (cp *CassandraProvider) Session() (Session, error) { - cluster := cp.newCluster() + cluster := cp.NewCluster() cluster.Keyspace = cp.Keyspace cluster.Consistency = gocql.Quorum sess, err := cluster.CreateSession() @@ -32,7 +32,7 @@ func (cp *CassandraProvider) Session() (Session, error) { const createKeySpace = `CREATE KEYSPACE IF NOT EXISTS %s WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };` func (cp *CassandraProvider) MigrationSession() (MigrationSession, error) { - cluster := cp.newCluster() + cluster := cp.NewCluster() cluster.Consistency = gocql.All sess, err := cluster.CreateSession() if err != nil { diff --git a/persistence_test.go b/persistence_test.go new file mode 100644 index 0000000..d6e0e73 --- /dev/null +++ b/persistence_test.go @@ -0,0 +1,221 @@ +package main_test + +import ( + "fmt" + + . "github.com/scoiatael/archai" + . "github.com/scoiatael/archai/persistence" + "github.com/scoiatael/archai/types" + + "github.com/gocql/gocql" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +const testingKeyspace = "archai_test" + +const findKeyspace = `select keyspace_name from system.schema_keyspaces where keyspace_name = ?` + +var ( + config Config + provider CassandraProvider + root_sess *gocql.Session +) + +func testingKeyspaceExists() (bool, error) { + var ( + err error + exists bool + ) + + iter := root_sess.Query(findKeyspace, testingKeyspace).Iter() + exists = iter.Scan(nil) + err = iter.Close() + return exists, err +} + +var dropKeyspace = fmt.Sprintf(`DROP KEYSPACE IF EXISTS %s`, testingKeyspace) + +func dropTestingKeyspace() error { + return root_sess.Query(dropKeyspace).Exec() +} + +var dropMigrations = fmt.Sprintf(`DROP TABLE IF EXISTS %s.archai_migrations`, testingKeyspace) + +func dropMigrationTable() error { + return root_sess.Query(dropMigrations).Exec() +} + +const findTable = `SELECT columnfamily_name from system.schema_columns where columnfamily_name = ? LIMIT 1 ALLOW FILTERING` + +func migrationTableExists() (bool, error) { + var ( + exists bool + err error + ) + + iter := root_sess.Query(findTable, "archai_migrations").Iter() + exists = iter.Scan(nil) + err = iter.Close() + return exists, err +} + +var _ = BeforeSuite(func() { + var err error + config = Config{Keyspace: testingKeyspace, Hosts: []string{"127.0.0.1"}} + provider = CassandraProvider{Hosts: config.Hosts, Keyspace: config.Keyspace} + cluster := provider.NewCluster() + cluster.Consistency = gocql.All + root_sess, err = cluster.CreateSession() + if err != nil { + panic(err) + } +}) + +var _ = AfterSuite(func() { + root_sess.Close() +}) + +var _ = Describe("Persistence", func() { + Describe("MigrationSession", func() { + BeforeEach(func() { + err := dropTestingKeyspace() + Expect(err).NotTo(HaveOccurred()) + }) + It("creates keyspace", func() { + var ( + exists bool + err error + ) + exists, err = testingKeyspaceExists() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeFalse()) + + sess, err := provider.MigrationSession() + Expect(err).NotTo(HaveOccurred()) + defer sess.Close() + + exists, err = testingKeyspaceExists() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeTrue()) + + }) + }) + Describe("ShouldRunMigration & DidRunMigration", func() { + var ( + err error + sess MigrationSession + ) + BeforeEach(func() { + sess, err = provider.MigrationSession() + Expect(err).NotTo(HaveOccurred()) + err = dropMigrationTable() + Expect(err).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + sess.Close() + }) + + Context("when there's no migration table", func() { + It("creates migration table", func() { + var ( + exists bool + ) + exists, err = migrationTableExists() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeFalse()) + + _, err := sess.ShouldRunMigration("foo") + Expect(err).NotTo(HaveOccurred()) + + exists, err = migrationTableExists() + Expect(err).NotTo(HaveOccurred()) + Expect(exists).To(BeTrue()) + }) + }) + + Context("when migrations were not run", func() { + It("returns true", func() { + should, err := sess.ShouldRunMigration("foo") + Expect(err).NotTo(HaveOccurred()) + Expect(should).To(BeTrue()) + }) + }) + + Context("after migration was run", func() { + It("returns false", func() { + err := sess.DidRunMigration("foo") + Expect(err).NotTo(HaveOccurred()) + + should, err := sess.ShouldRunMigration("foo") + Expect(err).NotTo(HaveOccurred()) + Expect(should).To(BeFalse()) + }) + }) + }) + + Describe("ReadEvents", func() { + var ( + sess Session + ) + BeforeEach(func() { + var ( + err error + ) + err = dropTestingKeyspace() + Expect(err).NotTo(HaveOccurred()) + s, err := provider.MigrationSession() + Expect(err).NotTo(HaveOccurred()) + defer s.Close() + err = CreateEventsTable.Run(s) + Expect(err).NotTo(HaveOccurred()) + + sess, err = provider.Session() + Expect(err).NotTo(HaveOccurred()) + }) + AfterEach(func() { + sess.Close() + }) + Context("when there are no events", func() { + It("returns empty array", func() { + es, err := sess.ReadEvents("test-stream", "00000000-0000-1000-8080-808080808080", 10) + Expect(err).NotTo(HaveOccurred()) + Expect(es).To(BeEmpty()) + }) + }) + Context("after some events were added", func() { + var ( + err error + events []types.Event + cursor string + ) + BeforeEach(func() { + err = sess.WriteEvent("test-stream", []byte(`{ "a": 1 }`), make(map[string]string)) + Expect(err).NotTo(HaveOccurred()) + err = sess.WriteEvent("test-stream", []byte(`{ "a": 2 }`), make(map[string]string)) + Expect(err).NotTo(HaveOccurred()) + cursor = "00000000-0000-1000-8080-808080808080" + events, err = sess.ReadEvents("test-stream", cursor, 10) + Expect(err).NotTo(HaveOccurred()) + }) + JustBeforeEach(func() { + events, err = sess.ReadEvents("test-stream", cursor, 10) + Expect(err).NotTo(HaveOccurred()) + }) + It("returns non-empty array", func() { + Expect(events).NotTo(BeEmpty()) + Expect(events).To(HaveLen(2)) + }) + Context("when given cursor", func() { + BeforeEach(func() { + cursor = events[0].ID + }) + It("returns events after cursor", func() { + Expect(events).NotTo(BeEmpty()) + Expect(events).To(HaveLen(1)) + }) + }) + }) + }) +}) diff --git a/version.go b/version.go index 0354e88..fae9aef 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,5 @@ package main +// Version of this app. +// Follow SemVer. const Version = "0.1.0"