diff --git a/search/search_test.go b/search/search_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..333a254b6a8a5807db4e2e0670bab6ae39f7b9f3
--- /dev/null
+++ b/search/search_test.go
@@ -0,0 +1,121 @@
+package search_test
+
+import (
+	"fmt"
+	"math/rand"
+	"sort"
+	"testing"
+	"time"
+
+	"gitlab.com/uafrica/go-utils/logger"
+	"gitlab.com/uafrica/go-utils/search"
+)
+
+func TestLocalWriter(t *testing.T) {
+	test(t, search.Config{
+		Addresses: []string{"https://localhost:9200"},
+	})
+}
+
+func TestDevWriter(t *testing.T) {
+	test(t, search.Config{
+		Addresses: []string{"https://search-uafrica-v3-api-logs-fefgiypvmb3sg5wqohgsbqnzvq.af-south-1.es.amazonaws.com/"}, //from AWS Console OpenSearch Service > Domains > uafrica-v3-api-logs > General Information: Domain Endpoints
+		Username:  "uafrica",
+		Password:  "Aiz}a4ee",
+	})
+}
+
+func test(t *testing.T, c search.Config) {
+	logger.SetGlobalFormat(logger.NewConsole())
+	logger.SetGlobalLevel(logger.LevelDebug)
+	a, err := search.New(c)
+	if err != nil {
+		t.Fatalf("failed to create writer: %+v", err)
+	}
+
+	indexName := "go-utils-audit-test"
+	ts, err := a.TimeSeries(indexName, testStruct{})
+	if err != nil {
+		t.Fatalf("failed to create time series: %+v", err)
+	}
+
+	//write N records
+	methods := []string{"GET", "POST", "GET", "PATCH", "GET", "GET", "DELETE", "GET", "GET"} //more gets than others
+	paths := []string{"/users", "/orders", "/accounts", "/shipment", "/rates", "/accounts", "/shipment", "/rates", "/accounts", "/shipment", "/rates", "/accounts", "/shipment", "/rates"}
+	N := 100
+	testTime := time.Now().Add(-time.Hour * time.Duration(N))
+	for i := 0; i < N; i++ {
+		testTime = testTime.Add(time.Duration(float64(rand.Intn(100)) / 60.0 * float64(time.Hour)))
+		method := methods[i%len(methods)]
+		path := paths[i%len(paths)]
+		if err := ts.Write(
+			testTime,
+			testTime.Add(-time.Duration(float64(time.Second)*(float64(rand.Intn(100))/100.0+0.1))),
+			testStruct{
+				TimeSeriesHeader: search.TimeSeriesHeader{},
+				Test1:            fmt.Sprintf("%d", i+1),          //1,2,3,...
+				Test2:            fmt.Sprintf("ACC_%05d", 93+i%7), //ACC_00093..ACC00100
+				Test3:            i%3 + 8,                         //8,9, or 10
+				HTTP: httpData{
+					Method: method,
+					Path:   path,
+				},
+				HTTPMethod: method,
+				HTTPPath:   path,
+			}); err != nil {
+			t.Fatalf("failed to add doc: %+v", err)
+		}
+	}
+
+	docs, totalCount, err := ts.Search(10)
+	if err != nil {
+		t.Errorf("failed to search: %+v", err)
+	} else {
+		t.Logf("search: %d: %+v", totalCount, docs)
+	}
+
+	oldList, err := a.DelOldTimeSeries(indexName, 2)
+	if err != nil {
+		t.Fatalf("failed to del old: %+v", err)
+	}
+	sort.Slice(oldList, func(i, j int) bool { return oldList[i] < oldList[j] })
+
+	t.Logf("Deleted %d old series", len(oldList))
+	//indexes deleted depends on current time, so not verifying
+	// if len(oldList) != 2 || oldList[0] != "go-utils-audit-test-20211029" || oldList[1] != "go-utils-audit-test-20211030" {
+	// 	t.Fatalf("Did not delete expected indices")
+	// }
+	t.Logf("Done")
+}
+
+type testStruct struct {
+	search.TimeSeriesHeader
+	Test1      string   `json:"test1"`
+	Test2      string   `json:"test2"`
+	Test3      int      `json:"test3"`
+	HTTP       httpData `json:"http"`
+	HTTPMethod string   `json:"http_method" search:"keyword"`
+	HTTPPath   string   `json:"http_path" search:"keyword"`
+}
+
+type httpData struct {
+	Method string `json:"method" search:"keyword"`
+	Path   string `json:"path" search:"keyword"`
+}
+
+func TestOlderThan(t *testing.T) {
+	local := time.Now().Location()
+
+	//time now for test (using a fixed value in SAST)
+	t0, _ := time.ParseInLocation("2006-01-02 15:04:05", "2021-10-20 14:15:16", local)
+	t.Logf("t0=%s", t0)
+
+	//threshold is 2 days older, applying at midnight in location SAST
+	olderThanDays := 2
+	t.Logf("n=%d", olderThanDays)
+
+	t1 := time.Date(t0.Year(), t0.Month(), t0.Day(), 0, 0, 0, 0, local)
+	t.Logf("t1=%s", t1)
+	t1 = t1.Add(-time.Hour * 24 * time.Duration(olderThanDays))
+	t.Logf("Threshold = %s", t1)
+}