Bläddra i källkod

WIP on eventinfo, with generic setUp()/tearDown() methods for DB.

Frederic G. MARAND 10 år sedan
förälder
incheckning
d42e1996ff
6 ändrade filer med 205 tillägg och 0 borttagningar
  1. 1 0
      .gitignore
  2. 17 0
      Makefile
  3. 16 0
      data/db-schema.sql
  4. 77 0
      storage/info.go
  5. 90 0
      storage/info_test.go
  6. 4 0
      storage/storage.go

+ 1 - 0
.gitignore

@@ -2,6 +2,7 @@
 .idea/
 
 # Compiled Object files, Static and Dynamic libs (Shared Objects)
+kurz
 *.o
 *.a
 *.so

+ 17 - 0
Makefile

@@ -0,0 +1,17 @@
+GCFLAGS=-gcflags "-N -l"
+
+all: kurz
+
+kurz:
+	go vet ./...
+	go build $(GCFLAGS) $<
+
+clean:
+	go clean -i ./...
+	find . -name coverage.out -exec rm {} \;
+
+install:
+	go install
+
+test:
+	go test -v ./...

+ 16 - 0
data/db-schema.sql

@@ -47,6 +47,22 @@ CREATE TABLE IF NOT EXISTS `shorturl` (
   KEY `longurl` (`longurl`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
 
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `eventinfo`
+--
+
+CREATE TABLE IF NOT EXISTS `eventinfo` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `source` varchar(8) DEFAULT NULL COMMENT 'The event source',
+  `ip` varchar(30) NOT NULL DEFAULT '' COMMENT 'May be IPv4 or IPv6',
+  UNIQUE KEY `id` (`id`),
+  KEY `ip` (`ip`),
+  KEY `source` (`source`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Event info' AUTO_INCREMENT=1 ;
+
 --
 -- Constraints for dumped tables
 --

+ 77 - 0
storage/info.go

@@ -1,4 +1,81 @@
 package storage
 
+import (
+	"net"
+	"time"
+)
+
 type EventInfo struct {
+	Id     int64
+	Source string
+	Ip     net.IP
+	ts     time.Time
+}
+
+type EventInfoStorage struct {
+}
+
+func (eis EventInfoStorage) Table() string {
+	return "eventinfo"
+}
+
+var EventInfoService EventInfoStorage = EventInfoStorage{}
+
+func EventInfoLoadFromId(id int64, storage Storage) (EventInfo, error) {
+	var ei EventInfo
+	var source string
+	var ip string
+	var ts int64
+
+	sql := `
+SELECT source, ip, ts
+FROM eventinfo
+WHERE id = ?
+	`
+
+	err := storage.DB.QueryRow(sql, id).Scan(&source, &ip, &ts)
+	if err != nil {
+		ei = EventInfo{
+			Id:     id,
+			Source: source,
+			Ip:     net.ParseIP(ip),
+			ts:     time.Unix(ts, 0),
+		}
+	}
+
+	return ei, err
+}
+
+/**
+Save() persists an EventInfo to the storage.
+
+  - if it already has an non-zero ID, Save() will attempt to update it, and set the Id in the event object
+  - otherwise it will attempt to insert it
+
+*/
+func (ei *EventInfo) Save(s Storage) error {
+	var err error
+
+	ip := ei.Ip.String()
+	ts := ei.ts.Unix()
+
+	if ei.Id == 0 {
+		sql := `
+INSERT INTO eventinfo(source, ip, ts)
+VALUES (?, ?, ?)
+		`
+		result, err := s.DB.Exec(sql, ei.Source, ip, ts)
+		if err == nil {
+			ei.Id, err = result.LastInsertId()
+		}
+	} else {
+		sql := `
+UPDATE eventinfo
+SET source = ?, ip = ?, ts = ?
+WHERE id = ?
+		`
+		_, err = s.DB.Exec(sql, ei.Source, ip, ts, ei.Id)
+	}
+
+	return err
 }

+ 90 - 0
storage/info_test.go

@@ -0,0 +1,90 @@
+package storage
+
+import (
+	"log"
+	"testing"
+)
+
+type void struct{}
+type voidSet map[string]void
+
+var purgeList voidSet
+
+/*
+setUp() initializes a test database.
+
+  - tablesToTruncate is a slice of names of tables to truncate during initialization.
+    If any of these tables are not present in the database, the function will fatal out.
+
+Use defer tearDown(t) to clean the database at the end of a test.
+
+Implementation note:
+
+In the current implementation, truncation is performed using SQL DELETE, not
+TRUNCATE, because MySQL 5.x cannot TRUNCATE the master table in a master/child
+referential integrity constraint, even when the child table is empty. This could
+smarter.
+
+// FIXME stop using constant credentials
+*/
+
+func setUp(t *testing.T, entitiesToPurge []StorageController) {
+	var err error
+	var DSN = "goroot:gopass@tcp(localhost:3306)/go_kurz_test"
+	var name string = ""
+	var voidNil void = void{}
+	var tableNames voidSet = make(voidSet)
+
+	Service.SetDSN(DSN)
+	err = Service.Open()
+	if err != nil {
+		t.Fatalf("Failed opening the test database: %+v", err)
+	}
+
+	sql := "SHOW TABLES"
+	rows, err := Service.DB.Query(sql)
+	if err != nil {
+		log.Fatalf("Failed listing tables in the database: %+v\n", err)
+	}
+	defer rows.Close()
+
+	purgeList = make(voidSet)
+	for rows.Next() {
+		if err = rows.Scan(&name); err != nil {
+			log.Fatalf("Failed scanning table name: %+v\n", err)
+		}
+		tableNames[name] = voidNil
+	}
+	log.Print("Tables:", tableNames)
+	for _, entityName := range entitiesToPurge {
+		tableName := entityName.Table()
+		log.Printf("Checking %s\n", tableName)
+		if _, ok := tableNames[tableName]; !ok {
+			log.Fatalf("Table %s can not be found in the database. Aborting\n", tableName)
+		}
+		purgeList[tableName] = voidNil
+		Service.Truncate(name)
+	}
+}
+
+func tearDown(t *testing.T) {
+	if purgeList == nil {
+		log.Fatalf("Truncate list may not be nil : it has been initialized in initTermStorage. Aborting")
+	}
+
+	for name, _ := range purgeList {
+		Service.Truncate(name)
+	}
+
+	Service.Close()
+}
+
+func TestSave(t *testing.T) {
+	setUp(t, []StorageController{EventInfoService})
+	defer tearDown(t)
+}
+
+func TestLoad(t *testing.T) {
+	setUp(t, []StorageController{EventInfoService})
+	defer tearDown(t)
+}

+ 4 - 0
storage/storage.go

@@ -79,6 +79,10 @@ func (s *Storage) SetDSN(dsn string) {
 	s.DSN = dsn
 }
 
+type StorageController interface {
+	Table() string
+}
+
 /*
 init() initializes the storage information from the command-line flag "dsn".
 */