Merge pull request #3 from 30x/pooja_xapid_377_testcases

Added test coverage for helpers, api, listener, init and buffer manager
diff --git a/api_helper_test.go b/api_helper_test.go
new file mode 100644
index 0000000..2ccc9f5
--- /dev/null
+++ b/api_helper_test.go
@@ -0,0 +1,112 @@
+package apidAnalytics
+
+import (
+	"bytes"
+	"encoding/json"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+// BeforeSuite setup and AfterSuite cleanup is in apidAnalytics_suite_test.go
+var _ = Describe("test valid() directly", func() {
+	Context("invalid record", func() {
+		It("should return invalid record", func() {
+			By("payload with missing required keys")
+
+			var record = []byte(`{
+						"response_status_code": 200,
+						"client_id":"testapikey"
+					}`)
+			raw := getRaw(record)
+			valid, e := validate(raw)
+
+			Expect(valid).To(Equal(false))
+			Expect(e.ErrorCode).To(Equal("MISSING_FIELD"))
+
+			By("payload with clst > clet")
+			record = []byte(`{
+						"response_status_code": 200,
+						"client_id":"testapikey",
+						"client_received_start_timestamp": 1486406248277,
+						"client_received_end_timestamp": 1486406248260
+					}`)
+			raw = getRaw(record)
+			valid, e = validate(raw)
+
+			Expect(valid).To(Equal(false))
+			Expect(e.ErrorCode).To(Equal("BAD_DATA"))
+			Expect(e.Reason).To(Equal("client_received_start_timestamp > client_received_end_timestamp"))
+
+		})
+	})
+	Context("valid record", func() {
+		It("should return true", func() {
+			var record = []byte(`{
+					"response_status_code": 200,
+					"client_id":"testapikey",
+					"client_received_start_timestamp": 1486406248277,
+					"client_received_end_timestamp": 1486406248290
+				}`)
+			raw := getRaw(record)
+			valid, _ := validate(raw)
+			Expect(valid).To(Equal(true))
+		})
+	})
+})
+
+var _ = Describe("test enrich() directly", func() {
+	Context("enrich record for existing apiKey", func() {
+		It("developer related fields should be added", func() {
+			var record = []byte(`{
+					"response_status_code": 200,
+					"client_id":"testapikey",
+					"client_received_start_timestamp": 1486406248277,
+					"client_received_end_timestamp": 1486406248290
+				}`)
+
+			raw := getRaw(record)
+			tenant := tenant{Org: "testorg", Env: "testenv", TenantId: "tenantid"}
+			enrich(raw, "testid", tenant)
+
+			Expect(raw["organization"]).To(Equal(tenant.Org))
+			Expect(raw["environment"]).To(Equal(tenant.Env))
+			Expect(raw["api_product"]).To(Equal("testproduct"))
+			Expect(raw["developer_app"]).To(Equal("testapp"))
+			Expect(raw["developer_email"]).To(Equal("testdeveloper@test.com"))
+			Expect(raw["developer"]).To(Equal("testdeveloper"))
+		})
+	})
+
+	Context("enrich record where no apikey is set", func() {
+		It("developer related fields should not be added", func() {
+			var record = []byte(`{
+					"response_status_code": 200,
+					"client_received_start_timestamp": 1486406248277,
+					"client_received_end_timestamp": 1486406248290
+				}`)
+
+			raw := getRaw(record)
+			tenant := tenant{Org: "testorg", Env: "testenv", TenantId: "tenantid"}
+			enrich(raw, "testid", tenant)
+
+			Expect(raw["organization"]).To(Equal(tenant.Org))
+			Expect(raw["environment"]).To(Equal(tenant.Env))
+			Expect(raw["api_product"]).To(BeNil())
+			Expect(raw["developer_app"]).To(BeNil())
+			Expect(raw["developer_email"]).To(BeNil())
+			Expect(raw["developer"]).To(BeNil())
+		})
+	})
+})
+
+func getRaw(record []byte) map[string]interface{} {
+	var raw map[string]interface{}
+
+	decoder := json.NewDecoder(bytes.NewReader(record)) // Decode payload to JSON data
+	decoder.UseNumber()
+	err := decoder.Decode(&raw)
+
+	Expect(err).ShouldNot(HaveOccurred())
+	return raw
+}
diff --git a/api_test.go b/api_test.go
index d172a29..83ce5a2 100644
--- a/api_test.go
+++ b/api_test.go
@@ -1,49 +1,203 @@
 package apidAnalytics
 
 import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
 	"net/http"
 	"net/url"
-	"strings"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 )
 
 // BeforeSuite setup and AfterSuite cleanup is in apidAnalytics_suite_test.go
-var _ = Describe("testing saveAnalyticsRecord() directly", func() {
-	Context("valid scopeuuid", func() {
-		It("should successfully return", func() {
+var _ = Describe("POST /analytics/{scopeuuid}", func() {
+	Context("invalid content type header", func() {
+		It("should return bad request", func() {
 			uri, err := url.Parse(testServer.URL)
-			uri.Path = analyticsBasePath
-
-			v := url.Values{}
-			v.Add("bundle_scope_uuid", "testid")
-
-			client := &http.Client{}
-			req, err := http.NewRequest("POST", uri.String(),
-				strings.NewReader(v.Encode()))
-			res, err := client.Do(req)
-			defer res.Body.Close()
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "test")
 			Expect(err).ShouldNot(HaveOccurred())
-			Expect(res.StatusCode, http.StatusOK)
+
+			req, _ := http.NewRequest("POST", uri.String(), nil)
+			req.Header.Set("Content-Type", "application/x-gzip")
+
+			res, e := makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusBadRequest))
+			Expect(e.ErrorCode).To(Equal("UNSUPPORTED_CONTENT_TYPE"))
+		})
+	})
+
+	Context("invalid content encoding header", func() {
+		It("should return bad request", func() {
+			uri, err := url.Parse(testServer.URL)
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "testid")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			req, _ := http.NewRequest("POST", uri.String(), nil)
+			req.Header.Set("Content-Type", "application/json")
+			req.Header.Set("Content-Encoding", "application/gzip")
+
+			res, e := makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusBadRequest))
+			Expect(e.ErrorCode).To(Equal("UNSUPPORTED_CONTENT_ENCODING"))
 		})
 	})
 
 	Context("invalid scopeuuid", func() {
 		It("should return bad request", func() {
 			uri, err := url.Parse(testServer.URL)
-			uri.Path = analyticsBasePath
-
-			v := url.Values{}
-			v.Add("bundle_scope_uuid", "wrongId")
-
-			client := &http.Client{}
-			req, err := http.NewRequest("POST", uri.String(),
-				strings.NewReader(v.Encode()))
-			res, err := client.Do(req)
-			defer res.Body.Close()
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "wrongid")
 			Expect(err).ShouldNot(HaveOccurred())
-			Expect(res.StatusCode, http.StatusBadRequest)
+
+			req, _ := http.NewRequest("POST", uri.String(), nil)
+			req.Header.Set("Content-Type", "application/json")
+
+			res, e := makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusBadRequest))
+			Expect(e.ErrorCode).To(Equal("UNKNOWN_SCOPE"))
+		})
+	})
+
+	Context("Unitialized DB", func() {
+		It("should return internal server error", func() {
+			db := getDB()
+			setDB(nil)
+
+			uri, err := url.Parse(testServer.URL)
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "testid")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			req, _ := http.NewRequest("POST", uri.String(), nil)
+			req.Header.Set("Content-Type", "application/json")
+
+			res, e := makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusInternalServerError))
+			Expect(e.ErrorCode).To(Equal("INTERNAL_SERVER_ERROR"))
+
+			setDB(db)
+
+		})
+	})
+
+	Context("bad payload", func() {
+		It("should return bad request", func() {
+
+			By("no payload")
+			uri, err := url.Parse(testServer.URL)
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "testid")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			req, _ := http.NewRequest("POST", uri.String(), nil)
+			req.Header.Set("Content-Type", "application/json")
+
+			res, e := makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusBadRequest))
+			Expect(e.ErrorCode).To(Equal("BAD_DATA"))
+			Expect(e.Reason).To(Equal("Not a valid JSON payload"))
+
+			By("payload with 0 records")
+			uri, err = url.Parse(testServer.URL)
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "testid")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			var payload = []byte(`{}`)
+			req, _ = http.NewRequest("POST", uri.String(), bytes.NewReader(payload))
+			req.Header.Set("Content-Type", "application/json")
+
+			res, e = makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusBadRequest))
+			Expect(e.ErrorCode).To(Equal("NO_RECORDS"))
+
+			By("set content encoding to gzip but send json data")
+			uri, err = url.Parse(testServer.URL)
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "testid")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			payload = []byte(`{
+					"records":[{
+						"response_status_code": 200,
+						"client_id":"testapikey"
+					}]
+				}`)
+
+			req, _ = http.NewRequest("POST", uri.String(), bytes.NewReader(payload))
+			req.Header.Set("Content-Type", "application/json")
+			req.Header.Set("Content-Encoding", "gzip")
+
+			res, e = makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusBadRequest))
+			Expect(e.ErrorCode).To(Equal("BAD_DATA"))
+			Expect(e.Reason).To(Equal("Gzip Encoded data cannot be read"))
+
+			By("1 bad record")
+			uri, err = url.Parse(testServer.URL)
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "testid")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			payload = []byte(`{
+						"records":[{
+							"response_status_code": 200,
+							"client_id":"testapikey",
+							"client_received_start_timestamp": 1486406248277,
+							"client_received_end_timestamp": 1486406248290
+						},{
+							"response_status_code": 200,
+							"client_id":"testapikey"
+						}]
+					}`)
+
+			req, _ = http.NewRequest("POST", uri.String(), bytes.NewReader(payload))
+			req.Header.Set("Content-Type", "application/json")
+
+			res, e = makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusBadRequest))
+			Expect(e.ErrorCode).To(Equal("MISSING_FIELD"))
+		})
+	})
+
+	Context("valid payload", func() {
+		It("should return successfully", func() {
+			uri, err := url.Parse(testServer.URL)
+			uri.Path = fmt.Sprintf(analyticsBasePath+"/%s", "testid")
+			Expect(err).ShouldNot(HaveOccurred())
+
+			var payload = []byte(`{
+					"records":[{
+						"response_status_code": 200,
+						"client_id":"testapikey",
+						"client_received_start_timestamp": 1486406248277,
+						"client_received_end_timestamp": 1486406248290
+					}]
+				}`)
+
+			req, _ := http.NewRequest("POST", uri.String(), bytes.NewReader(payload))
+			req.Header.Set("Content-Type", "application/json")
+
+			res, _ := makeRequest(req)
+
+			Expect(res.StatusCode).To(Equal(http.StatusOK))
 		})
 	})
 })
+
+func makeRequest(req *http.Request) (*http.Response, errResponse) {
+	res, err := client.Do(req)
+	defer res.Body.Close()
+	Expect(err).ShouldNot(HaveOccurred())
+
+	var body errResponse
+	respBody, _ := ioutil.ReadAll(res.Body)
+	json.Unmarshal(respBody, &body)
+
+	return res, body
+}
diff --git a/apidAnalytics_suite_test.go b/apidAnalytics_suite_test.go
index 1e5c128..345b4b8 100644
--- a/apidAnalytics_suite_test.go
+++ b/apidAnalytics_suite_test.go
@@ -26,36 +26,42 @@
 var _ = BeforeSuite(func() {
 	apid.Initialize(factory.DefaultServicesFactory())
 
-	config := apid.Config()
+	config = apid.Config()
 
 	var err error
 	testTempDir, err = ioutil.TempDir("", "api_test")
 	Expect(err).NotTo(HaveOccurred())
 
+	config.Set("local_storage_path", testTempDir)
 	config.Set("data_path", testTempDir)
-	config.Set(uapServerBase, "http://localhost:9000") // dummy value
-	config.Set("apigeesync_apid_instance_id",
-		"abcdefgh-ijkl-mnop-qrst-uvwxyz123456") // dummy value
-	config.Set(useCaching, true)
+	config.Set("apigeesync_apid_instance_id", "abcdefgh-ijkl-mnop-qrst-uvwxyz123456") // dummy value
 
 	db, err := apid.Data().DB()
 	Expect(err).NotTo(HaveOccurred())
-	setDB(db)
+
 	createApidClusterTables(db)
 	createTables(db)
 	insertTestData(db)
-	apid.InitializePlugins()
+	setDB(db)
 
-	// Create cache else its created in listener.go when a snapshot is received
+	// required config uapServerBase is not set, thus init should panic
+	Expect(apid.InitializePlugins).To(Panic())
+
+	config.Set(uapServerBase, "http://localhost:9000") // dummy value
+	Expect(apid.InitializePlugins).ToNot(Panic())
+
+	config.Set(useCaching, true)
 	createTenantCache()
-	createDeveloperInfoCache()
+	Expect(len(tenantCache)).To(Equal(1))
 
-	testServer = httptest.NewServer(http.HandlerFunc(
-		func(w http.ResponseWriter, req *http.Request) {
-			if req.URL.Path == analyticsBasePathDefault {
-				saveAnalyticsRecord(w, req)
-			}
-		}))
+	createDeveloperInfoCache()
+	Expect(len(developerInfoCache)).To(Equal(1))
+
+	router := apid.API().Router()
+	router.HandleFunc(analyticsBasePath+"/{bundle_scope_uuid}", func(w http.ResponseWriter, req *http.Request) {
+		saveAnalyticsRecord(w, req)
+	}).Methods("POST")
+	testServer = httptest.NewServer(router)
 })
 
 func createTables(db apid.DB) {
diff --git a/buffering_manager_test.go b/buffering_manager_test.go
new file mode 100644
index 0000000..1789317
--- /dev/null
+++ b/buffering_manager_test.go
@@ -0,0 +1,144 @@
+package apidAnalytics
+
+import (
+	"compress/gzip"
+	"encoding/json"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+var _ = Describe("test getBucketForTimestamp()", func() {
+	It("should return new bucket or existing bucket if created previously", func() {
+		t := time.Date(2017, 1, 20, 10, 24, 5, 0, time.Local)
+		tenant := tenant{Org: "testorg", Env: "testenv", TenantId: "tenantid"}
+
+		bucket, err := getBucketForTimestamp(t, tenant)
+		Expect(err).ShouldNot(HaveOccurred())
+
+		Expect(bucket.DirName).To(Equal("testorg~testenv~20170120102400"))
+		Expect(bucket.FileWriter).ToNot(BeNil())
+
+		fw := bucket.FileWriter
+		Expect(fw.file.Name()).To(ContainSubstring("20170120102400.20170120102600"))
+
+		// Should return existing bucket if same interval timestamp is passed
+		t2 := time.Date(2017, 1, 20, 10, 25, 5, 0, time.Local)
+		bucket, err = getBucketForTimestamp(t2, tenant)
+		Expect(err).ShouldNot(HaveOccurred())
+		Expect(bucket.DirName).To(Equal("testorg~testenv~20170120102400"))
+	})
+})
+
+var _ = Describe("test getRandomHex()", func() {
+	It("should return a 4 digit hex", func() {
+		r1 := getRandomHex()
+		Expect(len(r1)).To(Equal(4))
+	})
+
+	It("should return differe 4 digit hex each time", func() {
+		r1 := getRandomHex()
+		r2 := getRandomHex()
+
+		Expect(r1).NotTo(Equal(r2))
+	})
+})
+
+var _ = Describe("test createWriteAndCloseFile()", func() {
+	Context("Cannot create file", func() {
+		It("should return error", func() {
+			fileName := "testFile" + fileExtension
+			completeFilePath := filepath.Join(localAnalyticsTempDir, "fakedir", fileName)
+
+			_, err := createGzipFile(completeFilePath)
+			Expect(err).To((HaveOccurred()))
+		})
+
+	})
+	Context("Create file, write to it and close file", func() {
+		It("should save content to file and read correctly", func() {
+			fileName := "testFile" + fileExtension
+			completeFilePath := filepath.Join(localAnalyticsTempDir, fileName)
+
+			fw, err := createGzipFile(completeFilePath)
+			Expect(err).ToNot((HaveOccurred()))
+
+			var records = []byte(`{
+					"records":[{
+						"response_status_code": 200,
+						"client_id":"testapikey",
+						"client_received_start_timestamp": 1486406248277,
+						"client_received_end_timestamp": 1486406248290
+					}]
+				}`)
+
+			raw := getRaw(records)
+
+			writeGzipFile(fw, raw["records"].([]interface{}))
+			closeGzipFile(fw)
+
+			// Verify file was written to properly
+			f, err := os.Open(completeFilePath)
+			defer f.Close()
+			gzReader, err := gzip.NewReader(f)
+			defer gzReader.Close()
+			Expect(err).ToNot((HaveOccurred()))
+
+			var record map[string]interface{}
+			decoder := json.NewDecoder(gzReader) // Decode payload to JSON data
+			decoder.UseNumber()
+			err = decoder.Decode(&record)
+			Expect(err).ToNot((HaveOccurred()))
+
+			Expect(record["client_id"]).To(Equal("testapikey"))
+			Expect(record["response_status_code"]).To(Equal(json.Number("200")))
+			Expect(record["client_received_start_timestamp"]).To(Equal(json.Number("1486406248277")))
+			Expect(record["client_received_end_timestamp"]).To(Equal(json.Number("1486406248290")))
+		})
+	})
+})
+
+var _ = Describe("test closeBucketChannel()", func() {
+	Context("send close bucket event on channel", func() {
+		It("close file and move to staging dir", func() {
+			dirName := "testorg~testenv~20160101230000"
+			dirPath := filepath.Join(localAnalyticsTempDir, dirName)
+
+			err := os.Mkdir(dirPath, os.ModePerm)
+			Expect(err).ShouldNot(HaveOccurred())
+
+			fileName := "testFile" + fileExtension
+			completeFilePath := filepath.Join(dirPath, fileName)
+
+			fw, e := createGzipFile(completeFilePath)
+			Expect(e).ShouldNot(HaveOccurred())
+
+			bucket := bucket{DirName: dirName, FileWriter: fw}
+			closeBucketEvent <- bucket
+
+			// wait for it to close dir and move to staging
+			time.Sleep(time.Second * 2)
+
+			expectedDirPath := filepath.Join(localAnalyticsStagingDir, dirName)
+			status, _ := exists(expectedDirPath)
+			Expect(status).To(BeTrue())
+
+			expectedfilePath := filepath.Join(localAnalyticsStagingDir, dirName, fileName)
+			status, _ = exists(expectedfilePath)
+			Expect(status).To(BeTrue())
+		})
+	})
+})
+
+func exists(path string) (bool, error) {
+	_, err := os.Stat(path)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return true, err
+}
diff --git a/common_helper_test.go b/common_helper_test.go
index c691d52..31bdb31 100644
--- a/common_helper_test.go
+++ b/common_helper_test.go
@@ -1,48 +1,109 @@
 package apidAnalytics
 
 import (
+	"database/sql"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 )
 
 var _ = Describe("test getTenantForScope()", func() {
-	Context("get tenant for valid scopeuuid", func() {
-		It("should return testorg and testenv", func() {
-			tenant, dbError := getTenantForScope("testid")
-			Expect(dbError.Reason).To(Equal(""))
-			Expect(tenant.Org).To(Equal("testorg"))
-			Expect(tenant.Env).To(Equal("testenv"))
-			Expect(tenant.TenantId).To(Equal("tenantid"))
+	Context("with usecaching set to true", func() {
+		Context("get tenant for valid scopeuuid", func() {
+			It("should return testorg and testenv", func() {
+				tenant, dbError := getTenantForScope("testid")
+				Expect(dbError.Reason).To(Equal(""))
+				Expect(tenant.TenantId).To(Equal("tenantid"))
+			})
+		})
+
+		Context("get tenant for invalid scopeuuid", func() {
+			It("should return empty tenant and a db error", func() {
+				tenant, dbError := getTenantForScope("wrongid")
+				Expect(tenant.Org).To(Equal(""))
+				Expect(dbError.ErrorCode).To(Equal("UNKNOWN_SCOPE"))
+			})
 		})
 	})
 
-	Context("get tenant for invalid scopeuuid", func() {
-		It("should return empty tenant and a db error", func() {
-			tenant, dbError := getTenantForScope("wrongid")
-			Expect(tenant.Org).To(Equal(""))
-			Expect(dbError.ErrorCode).To(Equal("UNKNOWN_SCOPE"))
+	Context("with usecaching set to false", func() {
+		BeforeEach(func() {
+			config.Set(useCaching, false)
+		})
+		AfterEach(func() {
+			config.Set(useCaching, true)
+		})
+		Context("get tenant for valid scopeuuid", func() {
+			It("should return testorg and testenv", func() {
+				tenant, dbError := getTenantForScope("testid")
+				Expect(dbError.Reason).To(Equal(""))
+				Expect(tenant.Org).To(Equal("testorg"))
+			})
+		})
+
+		Context("get tenant for invalid scopeuuid", func() {
+			It("should return empty tenant and a db error", func() {
+				tenant, dbError := getTenantForScope("wrongid")
+				Expect(tenant.Org).To(Equal(""))
+				Expect(dbError.ErrorCode).To(Equal("UNKNOWN_SCOPE"))
+			})
 		})
 	})
 })
 
 var _ = Describe("test getDeveloperInfo()", func() {
-	Context("get developerInfo for valid tenantId and apikey", func() {
-		It("should return all right data", func() {
-			developerInfo := getDeveloperInfo("tenantid", "testapikey")
-			Expect(developerInfo.ApiProduct).To(Equal("testproduct"))
-			Expect(developerInfo.Developer).To(Equal("testdeveloper"))
-			Expect(developerInfo.DeveloperEmail).To(Equal("testdeveloper@test.com"))
-			Expect(developerInfo.DeveloperApp).To(Equal("testapp"))
+	Context("with usecaching set to true", func() {
+		Context("get developerInfo for valid tenantId and apikey", func() {
+			It("should return all right data", func() {
+				developerInfo := getDeveloperInfo("tenantid", "testapikey")
+				Expect(developerInfo.ApiProduct).To(Equal("testproduct"))
+				Expect(developerInfo.Developer).To(Equal("testdeveloper"))
+			})
+		})
+
+		Context("get developerInfo for invalid tenantId and apikey", func() {
+			It("should return all right data", func() {
+				developerInfo := getDeveloperInfo("wrongid", "wrongapikey")
+				Expect(developerInfo.ApiProduct).To(Equal(""))
+			})
 		})
 	})
 
-	Context("get developerInfo for invalid tenantId and apikey", func() {
-		It("should return all right data", func() {
-			developerInfo := getDeveloperInfo("wrongid", "wrongapikey")
-			Expect(developerInfo.ApiProduct).To(Equal(""))
-			Expect(developerInfo.Developer).To(Equal(""))
-			Expect(developerInfo.DeveloperEmail).To(Equal(""))
-			Expect(developerInfo.DeveloperApp).To(Equal(""))
+	Context("with usecaching set to false", func() {
+		BeforeEach(func() {
+			config.Set(useCaching, false)
+		})
+		AfterEach(func() {
+			config.Set(useCaching, true)
+		})
+		Context("get developerInfo for valid tenantId and apikey", func() {
+			It("should return all right data", func() {
+				developerInfo := getDeveloperInfo("tenantid", "testapikey")
+				Expect(developerInfo.ApiProduct).To(Equal("testproduct"))
+				Expect(developerInfo.Developer).To(Equal("testdeveloper"))
+			})
+		})
+		Context("get developerInfo for invalid tenantId and apikey", func() {
+			It("should return all right data", func() {
+				developerInfo := getDeveloperInfo("wrongid", "wrongapikey")
+				Expect(developerInfo.ApiProduct).To(Equal(""))
+			})
+		})
+	})
+})
+
+var _ = Describe("test getValuesIgnoringNull()", func() {
+	Context("Null sql value", func() {
+		It("should return empty string", func() {
+			a := sql.NullString{String: "null", Valid: false}
+			res := getValuesIgnoringNull(a)
+			Expect(res).To(Equal(""))
+		})
+	})
+	Context("not null sql value", func() {
+		It("should return  string", func() {
+			a := sql.NullString{String: "sql", Valid: true}
+			res := getValuesIgnoringNull(a)
+			Expect(res).To(Equal("sql"))
 		})
 	})
 })
diff --git a/init_test.go b/init_test.go
new file mode 100644
index 0000000..4c3649d
--- /dev/null
+++ b/init_test.go
@@ -0,0 +1,28 @@
+package apidAnalytics
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"path/filepath"
+)
+
+var _ = Describe("test createDirectories()", func() {
+	Context("Parent directory exists", func() {
+		It("should create sub directory", func() {
+			subDir := filepath.Join(config.GetString("data_path"), "subDir")
+			directories := []string{subDir}
+
+			err := createDirectories(directories)
+			Expect(err).NotTo(HaveOccurred())
+		})
+	})
+	Context("Parent directory does not exists", func() {
+		subDir := filepath.Join("/fakepath", "subDir")
+		directories := []string{subDir}
+
+		It("sub directory creation should fail", func() {
+			err := createDirectories(directories)
+			Expect(err).To(HaveOccurred())
+		})
+	})
+})
diff --git a/listener_test.go b/listener_test.go
new file mode 100644
index 0000000..b792133
--- /dev/null
+++ b/listener_test.go
@@ -0,0 +1,140 @@
+package apidAnalytics
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	"github.com/apigee-labs/transicator/common"
+)
+
+const (
+	LISTENER_TABLE_APP_CRED_MAPPER = "kms.app_credential_apiproduct_mapper"
+	LISTENER_TABLE_DATA_SCOPE      = "edgex.data_scope"
+)
+
+var _ = Describe("listener", func() {
+	Context("Process changeList", func() {
+		Context(LISTENER_TABLE_DATA_SCOPE, func() {
+			It("insert/delete event should add/remove to/from cache if usecaching is true", func() {
+				config.Set(useCaching, true)
+				txn, err := getDB().Begin()
+				Expect(err).ShouldNot(HaveOccurred())
+				txn.Exec("INSERT INTO DATA_SCOPE (id, _change_selector, apid_cluster_id, scope, org, env) "+
+					"VALUES"+
+					"($1,$2,$3,$4,$5,$6)",
+					"i2",
+					"c2",
+					"a2",
+					"s2",
+					"o2",
+					"e2",
+				)
+				txn.Commit()
+
+				insert := common.ChangeList{
+					LastSequence: "test",
+					Changes: []common.Change{
+						{
+							Operation: common.Insert,
+							Table:     LISTENER_TABLE_DATA_SCOPE,
+							NewRow: common.Row{
+								"id":               &common.ColumnVal{Value: "i2"},
+								"_change_selector": &common.ColumnVal{Value: "c2"},
+								"apid_cluster_id":  &common.ColumnVal{Value: "a2"},
+								"scope":            &common.ColumnVal{Value: "s2"},
+								"org":              &common.ColumnVal{Value: "o2"},
+								"env":              &common.ColumnVal{Value: "e2"},
+								"created":          &common.ColumnVal{Value: "c2"},
+								"created_by":       &common.ColumnVal{Value: "c2"},
+								"updated":          &common.ColumnVal{Value: "u2"},
+								"updated_by":       &common.ColumnVal{Value: "u2"},
+							},
+						},
+					},
+				}
+
+				processChange(&insert)
+				tenant := tenantCache["i2"]
+				Expect(tenant.TenantId).To(Equal("s2"))
+				Expect(tenant.Org).To(Equal("o2"))
+				Expect(tenant.Env).To(Equal("e2"))
+
+				txn, err = getDB().Begin()
+				Expect(err).ShouldNot(HaveOccurred())
+				txn.Exec("DELETE FROM DATA_SCOPE where id = 'i2'")
+				txn.Commit()
+
+				delete := common.ChangeList{
+					LastSequence: "test",
+					Changes: []common.Change{
+						{
+							Operation: common.Delete,
+							Table:     LISTENER_TABLE_DATA_SCOPE,
+							OldRow:    insert.Changes[0].NewRow,
+						},
+					},
+				}
+
+				processChange(&delete)
+				_, exists := tenantCache["i2"]
+				Expect(exists).To(Equal(false))
+
+			})
+		})
+		Context(LISTENER_TABLE_APP_CRED_MAPPER, func() {
+			It("insert/delete event should refresh developer cache if usecaching is true", func() {
+				config.Set(useCaching, true)
+
+				txn, err := getDB().Begin()
+				Expect(err).ShouldNot(HaveOccurred())
+				txn.Exec("INSERT INTO APP_CREDENTIAL_APIPRODUCT_MAPPER (tenant_id, appcred_id, app_id, apiprdt_id, status, _change_selector) "+
+					"VALUES"+
+					"($1,$2,$3,$4,$5,$6)",
+					"tenantid",
+					"aci",
+					"ai",
+					"testproductid",
+					"APPROVED",
+					"12345",
+				)
+
+				txn.Exec("INSERT INTO APP (id, tenant_id, name, developer_id) "+
+					"VALUES"+
+					"($1,$2,$3,$4)",
+					"ai",
+					"tenantid",
+					"name",
+					"testdeveloperid",
+				)
+				txn.Commit()
+
+				insert := common.ChangeList{
+					LastSequence: "test",
+					Changes: []common.Change{
+						{
+							Operation: common.Insert,
+							Table:     LISTENER_TABLE_APP_CRED_MAPPER,
+							NewRow: common.Row{
+								"tenant_id":        &common.ColumnVal{Value: "tenantid"},
+								"appcred_id":       &common.ColumnVal{Value: "aci"},
+								"app_id":           &common.ColumnVal{Value: "ai"},
+								"apiprdt_id":       &common.ColumnVal{Value: "api"},
+								"status":           &common.ColumnVal{Value: "s"},
+								"_change_selector": &common.ColumnVal{Value: "c"},
+							},
+						},
+					},
+				}
+
+				processChange(&insert)
+				key := getKeyForDeveloperInfoCache("tenantid", "aci")
+				developerInfo := developerInfoCache[key]
+
+				Expect(developerInfo.ApiProduct).To(Equal("testproduct"))
+				Expect(developerInfo.Developer).To(Equal("testdeveloper"))
+				Expect(developerInfo.DeveloperEmail).To(Equal("testdeveloper@test.com"))
+				Expect(developerInfo.DeveloperApp).To(Equal("name"))
+			})
+		})
+	})
+})