2022-01-09 06:59:28 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2022-11-26 05:04:40 +01:00
|
|
|
"context"
|
2022-01-09 06:59:28 +01:00
|
|
|
"encoding/json"
|
2022-11-27 20:59:06 +01:00
|
|
|
"io"
|
2022-01-09 06:59:28 +01:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2022-12-12 04:42:51 +01:00
|
|
|
"os"
|
2022-04-09 22:02:30 +02:00
|
|
|
"strings"
|
2022-01-09 06:59:28 +01:00
|
|
|
"testing"
|
2022-09-22 06:46:46 +02:00
|
|
|
"time"
|
2022-01-09 06:59:28 +01:00
|
|
|
|
2022-04-08 05:59:40 +02:00
|
|
|
"github.com/ddworken/hishtory/client/data"
|
2022-01-09 06:59:28 +01:00
|
|
|
"github.com/ddworken/hishtory/shared"
|
2022-10-28 06:53:47 +02:00
|
|
|
"github.com/ddworken/hishtory/shared/testutils"
|
2022-09-22 06:46:46 +02:00
|
|
|
"github.com/go-test/deep"
|
2022-04-07 03:18:46 +02:00
|
|
|
"github.com/google/uuid"
|
2022-11-27 07:40:43 +01:00
|
|
|
"gorm.io/gorm"
|
2022-01-09 06:59:28 +01:00
|
|
|
)
|
|
|
|
|
2022-04-03 07:54:09 +02:00
|
|
|
func TestESubmitThenQuery(t *testing.T) {
|
|
|
|
// Set up
|
|
|
|
InitDB()
|
|
|
|
|
2022-04-03 19:08:18 +02:00
|
|
|
// Register a few devices
|
2022-04-08 05:59:40 +02:00
|
|
|
userId := data.UserId("key")
|
2022-04-04 06:00:46 +02:00
|
|
|
devId1 := uuid.Must(uuid.NewRandom()).String()
|
|
|
|
devId2 := uuid.Must(uuid.NewRandom()).String()
|
2022-04-08 05:59:40 +02:00
|
|
|
otherUser := data.UserId("otherkey")
|
2022-04-04 06:00:46 +02:00
|
|
|
otherDev := uuid.Must(uuid.NewRandom()).String()
|
2022-04-03 19:08:18 +02:00
|
|
|
deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-04-03 19:08:18 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-04-03 19:08:18 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev+"&user_id="+otherUser, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-04-03 19:08:18 +02:00
|
|
|
|
2022-04-03 07:54:09 +02:00
|
|
|
// Submit a few entries for different devices
|
2022-10-28 06:53:47 +02:00
|
|
|
entry := testutils.MakeFakeHistoryEntry("ls ~/")
|
2022-04-08 05:59:40 +02:00
|
|
|
encEntry, err := data.EncryptHistoryEntry("key", entry)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-03 07:54:09 +02:00
|
|
|
reqBody, err := json.Marshal([]shared.EncHistoryEntry{encEntry})
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-03 07:54:09 +02:00
|
|
|
submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
2023-09-07 15:43:04 +02:00
|
|
|
apiSubmitHandler(httptest.NewRecorder(), submitReq)
|
2022-04-03 07:54:09 +02:00
|
|
|
|
|
|
|
// Query for device id 1
|
|
|
|
w := httptest.NewRecorder()
|
2022-04-17 08:02:36 +02:00
|
|
|
searchReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiQueryHandler(w, searchReq)
|
2022-04-03 07:54:09 +02:00
|
|
|
res := w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err := io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-03 07:54:09 +02:00
|
|
|
var retrievedEntries []*shared.EncHistoryEntry
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &retrievedEntries))
|
2022-04-03 07:54:09 +02:00
|
|
|
if len(retrievedEntries) != 1 {
|
|
|
|
t.Fatalf("Expected to retrieve 1 entry, found %d", len(retrievedEntries))
|
|
|
|
}
|
|
|
|
dbEntry := retrievedEntries[0]
|
2022-04-04 06:00:46 +02:00
|
|
|
if dbEntry.DeviceId != devId1 {
|
2022-04-03 07:54:09 +02:00
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-04-08 05:59:40 +02:00
|
|
|
if dbEntry.UserId != data.UserId("key") {
|
2022-04-03 07:54:09 +02:00
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-11-26 21:10:18 +01:00
|
|
|
if dbEntry.ReadCount != 0 {
|
2022-04-03 07:54:09 +02:00
|
|
|
t.Fatalf("db.ReadCount should have been 1, was %v", dbEntry.ReadCount)
|
|
|
|
}
|
2022-04-08 05:59:40 +02:00
|
|
|
decEntry, err := data.DecryptHistoryEntry("key", *dbEntry)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-08 05:59:40 +02:00
|
|
|
if !data.EntryEquals(decEntry, entry) {
|
2022-04-05 07:07:01 +02:00
|
|
|
t.Fatalf("DB data is different than input! \ndb =%#v\ninput=%#v", *dbEntry, entry)
|
2022-04-03 07:54:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Same for device id 2
|
|
|
|
w = httptest.NewRecorder()
|
2022-04-17 08:02:36 +02:00
|
|
|
searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiQueryHandler(w, searchReq)
|
2022-04-03 07:54:09 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &retrievedEntries))
|
2022-04-03 07:54:09 +02:00
|
|
|
if len(retrievedEntries) != 1 {
|
|
|
|
t.Fatalf("Expected to retrieve 1 entry, found %d", len(retrievedEntries))
|
|
|
|
}
|
|
|
|
dbEntry = retrievedEntries[0]
|
2022-04-04 06:00:46 +02:00
|
|
|
if dbEntry.DeviceId != devId2 {
|
2022-04-03 07:54:09 +02:00
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-04-08 05:59:40 +02:00
|
|
|
if dbEntry.UserId != data.UserId("key") {
|
2022-04-03 07:54:09 +02:00
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-11-26 21:10:18 +01:00
|
|
|
if dbEntry.ReadCount != 0 {
|
2022-04-03 07:54:09 +02:00
|
|
|
t.Fatalf("db.ReadCount should have been 1, was %v", dbEntry.ReadCount)
|
|
|
|
}
|
2022-04-08 05:59:40 +02:00
|
|
|
decEntry, err = data.DecryptHistoryEntry("key", *dbEntry)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-08 05:59:40 +02:00
|
|
|
if !data.EntryEquals(decEntry, entry) {
|
2022-04-05 07:07:01 +02:00
|
|
|
t.Fatalf("DB data is different than input! \ndb =%#v\ninput=%#v", *dbEntry, entry)
|
2022-04-03 07:54:09 +02:00
|
|
|
}
|
2022-04-28 20:05:56 +02:00
|
|
|
|
|
|
|
// Bootstrap handler should return 2 entries, one for each device
|
|
|
|
w = httptest.NewRecorder()
|
|
|
|
searchReq = httptest.NewRequest(http.MethodGet, "/?user_id="+data.UserId("key")+"&device_id="+devId1, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiBootstrapHandler(w, searchReq)
|
2022-04-28 20:05:56 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &retrievedEntries))
|
2022-04-28 20:05:56 +02:00
|
|
|
if len(retrievedEntries) != 2 {
|
|
|
|
t.Fatalf("Expected to retrieve 2 entries, found %d", len(retrievedEntries))
|
|
|
|
}
|
2022-11-27 07:40:43 +01:00
|
|
|
|
|
|
|
// Assert that we aren't leaking connections
|
|
|
|
assertNoLeakedConnections(t, GLOBAL_DB)
|
2022-04-28 19:56:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDumpRequestAndResponse(t *testing.T) {
|
|
|
|
// Set up
|
|
|
|
InitDB()
|
|
|
|
|
|
|
|
// Register a first device for two different users
|
|
|
|
userId := data.UserId("dkey")
|
|
|
|
devId1 := uuid.Must(uuid.NewRandom()).String()
|
2022-05-23 04:45:46 +02:00
|
|
|
devId2 := uuid.Must(uuid.NewRandom()).String()
|
2022-04-28 19:56:59 +02:00
|
|
|
otherUser := data.UserId("dOtherkey")
|
|
|
|
otherDev1 := uuid.Must(uuid.NewRandom()).String()
|
2022-05-23 04:45:46 +02:00
|
|
|
otherDev2 := uuid.Must(uuid.NewRandom()).String()
|
2022-04-28 19:56:59 +02:00
|
|
|
deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-05-23 04:45:46 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-04-28 19:56:59 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev1+"&user_id="+otherUser, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-05-23 04:45:46 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev2+"&user_id="+otherUser, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-04-28 19:56:59 +02:00
|
|
|
|
|
|
|
// Query for dump requests, there should be one for userId
|
|
|
|
w := httptest.NewRecorder()
|
2023-09-07 15:43:04 +02:00
|
|
|
apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId1, nil))
|
2022-04-28 19:56:59 +02:00
|
|
|
res := w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err := io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 20:46:14 +02:00
|
|
|
var dumpRequests []*shared.DumpRequest
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &dumpRequests))
|
2022-04-28 19:56:59 +02:00
|
|
|
if len(dumpRequests) != 1 {
|
|
|
|
t.Fatalf("expected one pending dump request, got %#v", dumpRequests)
|
|
|
|
}
|
|
|
|
dumpRequest := dumpRequests[0]
|
2022-05-23 04:45:46 +02:00
|
|
|
if dumpRequest.RequestingDeviceId != devId2 {
|
2022-04-28 19:56:59 +02:00
|
|
|
t.Fatalf("unexpected device ID")
|
|
|
|
}
|
|
|
|
if dumpRequest.UserId != userId {
|
|
|
|
t.Fatalf("unexpected user ID")
|
|
|
|
}
|
|
|
|
|
|
|
|
// And one for otherUser
|
|
|
|
w = httptest.NewRecorder()
|
2023-09-07 15:43:04 +02:00
|
|
|
apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+otherUser+"&device_id="+otherDev1, nil))
|
2022-04-28 19:56:59 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 20:46:14 +02:00
|
|
|
dumpRequests = make([]*shared.DumpRequest, 0)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &dumpRequests))
|
2022-04-28 19:56:59 +02:00
|
|
|
if len(dumpRequests) != 1 {
|
|
|
|
t.Fatalf("expected one pending dump request, got %#v", dumpRequests)
|
|
|
|
}
|
|
|
|
dumpRequest = dumpRequests[0]
|
2022-05-23 04:45:46 +02:00
|
|
|
if dumpRequest.RequestingDeviceId != otherDev2 {
|
2022-04-28 19:56:59 +02:00
|
|
|
t.Fatalf("unexpected device ID")
|
|
|
|
}
|
|
|
|
if dumpRequest.UserId != otherUser {
|
|
|
|
t.Fatalf("unexpected user ID")
|
|
|
|
}
|
2022-04-03 07:54:09 +02:00
|
|
|
|
2022-04-28 19:56:59 +02:00
|
|
|
// And none if we query for a user ID that doesn't exit
|
|
|
|
w = httptest.NewRecorder()
|
2023-09-07 15:43:04 +02:00
|
|
|
apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id=foo&device_id=bar", nil))
|
2022-04-28 19:56:59 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 19:56:59 +02:00
|
|
|
if string(respBody) != "[]" {
|
|
|
|
t.Fatalf("got unexpected respBody: %#v", string(respBody))
|
|
|
|
}
|
|
|
|
|
2022-05-23 04:45:46 +02:00
|
|
|
// And none for a missing user ID
|
|
|
|
w = httptest.NewRecorder()
|
2023-09-07 15:43:04 +02:00
|
|
|
apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id=%20&device_id=%20", nil))
|
2022-05-23 04:45:46 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-05-23 04:45:46 +02:00
|
|
|
if string(respBody) != "[]" {
|
|
|
|
t.Fatalf("got unexpected respBody: %#v", string(respBody))
|
|
|
|
}
|
|
|
|
|
2022-04-28 19:56:59 +02:00
|
|
|
// Now submit a dump for userId
|
2022-10-28 06:53:47 +02:00
|
|
|
entry1Dec := testutils.MakeFakeHistoryEntry("ls ~/")
|
2022-04-28 19:56:59 +02:00
|
|
|
entry1, err := data.EncryptHistoryEntry("dkey", entry1Dec)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
|
|
|
entry2Dec := testutils.MakeFakeHistoryEntry("aaaaaaáaaa")
|
2022-04-28 19:56:59 +02:00
|
|
|
entry2, err := data.EncryptHistoryEntry("dkey", entry1Dec)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 19:56:59 +02:00
|
|
|
reqBody, err := json.Marshal([]shared.EncHistoryEntry{entry1, entry2})
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-10-01 18:50:06 +02:00
|
|
|
submitReq := httptest.NewRequest(http.MethodPost, "/?user_id="+userId+"&requesting_device_id="+devId2+"&source_device_id="+devId1, bytes.NewReader(reqBody))
|
2023-09-07 15:43:04 +02:00
|
|
|
apiSubmitDumpHandler(httptest.NewRecorder(), submitReq)
|
2022-04-28 19:56:59 +02:00
|
|
|
|
2022-06-05 08:03:05 +02:00
|
|
|
// Check that the dump request is no longer there for userId for either device ID
|
|
|
|
w = httptest.NewRecorder()
|
2023-09-07 15:43:04 +02:00
|
|
|
apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId1, nil))
|
2022-06-05 08:03:05 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-06-05 08:03:05 +02:00
|
|
|
if string(respBody) != "[]" {
|
|
|
|
t.Fatalf("got unexpected respBody: %#v", string(respBody))
|
|
|
|
}
|
2022-04-28 19:56:59 +02:00
|
|
|
w = httptest.NewRecorder()
|
2022-06-05 08:03:05 +02:00
|
|
|
// The other user
|
2023-09-07 15:43:04 +02:00
|
|
|
apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId2, nil))
|
2022-04-28 19:56:59 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 19:56:59 +02:00
|
|
|
if string(respBody) != "[]" {
|
|
|
|
t.Fatalf("got unexpected respBody: %#v", string(respBody))
|
|
|
|
}
|
|
|
|
|
|
|
|
// But it is there for the other user
|
|
|
|
w = httptest.NewRecorder()
|
2023-09-07 15:43:04 +02:00
|
|
|
apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+otherUser+"&device_id="+otherDev1, nil))
|
2022-04-28 19:56:59 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 20:46:14 +02:00
|
|
|
dumpRequests = make([]*shared.DumpRequest, 0)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &dumpRequests))
|
2022-04-28 19:56:59 +02:00
|
|
|
if len(dumpRequests) != 1 {
|
|
|
|
t.Fatalf("expected one pending dump request, got %#v", dumpRequests)
|
|
|
|
}
|
|
|
|
dumpRequest = dumpRequests[0]
|
2022-05-23 04:45:46 +02:00
|
|
|
if dumpRequest.RequestingDeviceId != otherDev2 {
|
2022-04-28 19:56:59 +02:00
|
|
|
t.Fatalf("unexpected device ID")
|
|
|
|
}
|
|
|
|
if dumpRequest.UserId != otherUser {
|
|
|
|
t.Fatalf("unexpected user ID")
|
|
|
|
}
|
|
|
|
|
|
|
|
// And finally, query to ensure that the dumped entries are in the DB
|
|
|
|
w = httptest.NewRecorder()
|
2022-05-23 04:45:46 +02:00
|
|
|
searchReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiQueryHandler(w, searchReq)
|
2022-04-28 19:56:59 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 19:56:59 +02:00
|
|
|
var retrievedEntries []*shared.EncHistoryEntry
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &retrievedEntries))
|
2022-04-03 19:08:18 +02:00
|
|
|
if len(retrievedEntries) != 2 {
|
|
|
|
t.Fatalf("Expected to retrieve 2 entries, found %d", len(retrievedEntries))
|
2022-04-03 07:54:09 +02:00
|
|
|
}
|
2022-04-28 19:56:59 +02:00
|
|
|
for _, dbEntry := range retrievedEntries {
|
2022-05-23 04:45:46 +02:00
|
|
|
if dbEntry.DeviceId != devId2 {
|
2022-04-28 19:56:59 +02:00
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
|
|
|
if dbEntry.UserId != userId {
|
|
|
|
t.Fatalf("Response contains an incorrect user ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-11-26 21:10:18 +01:00
|
|
|
if dbEntry.ReadCount != 0 {
|
2022-04-28 19:56:59 +02:00
|
|
|
t.Fatalf("db.ReadCount should have been 1, was %v", dbEntry.ReadCount)
|
|
|
|
}
|
|
|
|
decEntry, err := data.DecryptHistoryEntry("dkey", *dbEntry)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-04-28 19:56:59 +02:00
|
|
|
if !data.EntryEquals(decEntry, entry1Dec) && !data.EntryEquals(decEntry, entry2Dec) {
|
|
|
|
t.Fatalf("DB data is different than input! \ndb =%#v\nentry1=%#v\nentry2=%#v", *dbEntry, entry1Dec, entry2Dec)
|
|
|
|
}
|
|
|
|
}
|
2022-11-27 07:40:43 +01:00
|
|
|
|
|
|
|
// Assert that we aren't leaking connections
|
|
|
|
assertNoLeakedConnections(t, GLOBAL_DB)
|
2022-04-03 07:54:09 +02:00
|
|
|
}
|
2022-04-09 21:57:58 +02:00
|
|
|
|
2022-04-09 22:02:30 +02:00
|
|
|
func TestUpdateReleaseVersion(t *testing.T) {
|
2022-10-28 06:53:47 +02:00
|
|
|
if !testutils.IsOnline() {
|
2022-04-28 20:26:55 +02:00
|
|
|
t.Skip("skipping because we're currently offline")
|
|
|
|
}
|
|
|
|
|
2022-04-09 22:02:30 +02:00
|
|
|
// Set up
|
|
|
|
InitDB()
|
|
|
|
|
|
|
|
// Check that ReleaseVersion hasn't been set yet
|
|
|
|
if ReleaseVersion != "UNKNOWN" {
|
2022-04-14 06:30:27 +02:00
|
|
|
t.Fatalf("initial ReleaseVersion isn't as expected: %#v", ReleaseVersion)
|
2022-04-09 22:02:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update it
|
|
|
|
err := updateReleaseVersion()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("updateReleaseVersion failed: %v", err)
|
|
|
|
}
|
|
|
|
|
2022-04-15 05:18:49 +02:00
|
|
|
// If ReleaseVersion is still unknown, skip because we're getting rate limited
|
|
|
|
if ReleaseVersion == "UNKNOWN" {
|
|
|
|
t.Skip()
|
|
|
|
}
|
|
|
|
// Otherwise, check that the new value looks reasonable
|
2022-04-09 22:02:30 +02:00
|
|
|
if !strings.HasPrefix(ReleaseVersion, "v0.") {
|
2022-04-14 06:30:27 +02:00
|
|
|
t.Fatalf("ReleaseVersion wasn't updated to contain a version: %#v", ReleaseVersion)
|
2022-04-09 22:02:30 +02:00
|
|
|
}
|
2022-11-27 07:40:43 +01:00
|
|
|
|
|
|
|
// Assert that we aren't leaking connections
|
|
|
|
assertNoLeakedConnections(t, GLOBAL_DB)
|
2022-04-09 22:02:30 +02:00
|
|
|
}
|
2022-09-22 04:59:31 +02:00
|
|
|
|
2022-09-22 06:46:46 +02:00
|
|
|
func TestDeletionRequests(t *testing.T) {
|
|
|
|
// Set up
|
|
|
|
InitDB()
|
|
|
|
|
|
|
|
// Register two devices for two different users
|
|
|
|
userId := data.UserId("dkey")
|
|
|
|
devId1 := uuid.Must(uuid.NewRandom()).String()
|
|
|
|
devId2 := uuid.Must(uuid.NewRandom()).String()
|
|
|
|
otherUser := data.UserId("dOtherkey")
|
|
|
|
otherDev1 := uuid.Must(uuid.NewRandom()).String()
|
|
|
|
otherDev2 := uuid.Must(uuid.NewRandom()).String()
|
|
|
|
deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev1+"&user_id="+otherUser, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev2+"&user_id="+otherUser, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
|
|
|
|
// Add an entry for user1
|
2022-10-28 06:53:47 +02:00
|
|
|
entry1 := testutils.MakeFakeHistoryEntry("ls ~/")
|
2022-09-22 06:46:46 +02:00
|
|
|
entry1.DeviceId = devId1
|
|
|
|
encEntry, err := data.EncryptHistoryEntry("dkey", entry1)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
reqBody, err := json.Marshal([]shared.EncHistoryEntry{encEntry})
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
2023-09-07 15:43:04 +02:00
|
|
|
apiSubmitHandler(httptest.NewRecorder(), submitReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
|
|
|
|
// And another entry for user1
|
2022-10-28 06:53:47 +02:00
|
|
|
entry2 := testutils.MakeFakeHistoryEntry("ls /foo/bar")
|
2022-09-22 06:46:46 +02:00
|
|
|
entry2.DeviceId = devId2
|
|
|
|
encEntry, err = data.EncryptHistoryEntry("dkey", entry2)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
reqBody, err = json.Marshal([]shared.EncHistoryEntry{encEntry})
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
submitReq = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
2023-09-07 15:43:04 +02:00
|
|
|
apiSubmitHandler(httptest.NewRecorder(), submitReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
|
|
|
|
// And an entry for user2 that has the same timestamp as the previous entry
|
2022-10-28 06:53:47 +02:00
|
|
|
entry3 := testutils.MakeFakeHistoryEntry("ls /foo/bar")
|
2022-09-22 06:46:46 +02:00
|
|
|
entry3.StartTime = entry1.StartTime
|
|
|
|
entry3.EndTime = entry1.EndTime
|
|
|
|
encEntry, err = data.EncryptHistoryEntry("dOtherkey", entry3)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
reqBody, err = json.Marshal([]shared.EncHistoryEntry{encEntry})
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
submitReq = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
2023-09-07 15:43:04 +02:00
|
|
|
apiSubmitHandler(httptest.NewRecorder(), submitReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
|
|
|
|
// Query for device id 1
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
searchReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiQueryHandler(w, searchReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
res := w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err := io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
var retrievedEntries []*shared.EncHistoryEntry
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &retrievedEntries))
|
2022-09-22 06:46:46 +02:00
|
|
|
if len(retrievedEntries) != 2 {
|
|
|
|
t.Fatalf("Expected to retrieve 1 entry, found %d", len(retrievedEntries))
|
|
|
|
}
|
|
|
|
for _, dbEntry := range retrievedEntries {
|
|
|
|
if dbEntry.DeviceId != devId1 {
|
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
|
|
|
if dbEntry.UserId != data.UserId("dkey") {
|
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-11-26 21:10:18 +01:00
|
|
|
if dbEntry.ReadCount != 0 {
|
2022-09-22 06:46:46 +02:00
|
|
|
t.Fatalf("db.ReadCount should have been 1, was %v", dbEntry.ReadCount)
|
|
|
|
}
|
|
|
|
decEntry, err := data.DecryptHistoryEntry("dkey", *dbEntry)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
if !data.EntryEquals(decEntry, entry1) && !data.EntryEquals(decEntry, entry2) {
|
|
|
|
t.Fatalf("DB data is different than input! \ndb =%#v\nentry1=%#v\nentry2=%#v", *dbEntry, entry1, entry2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Submit a redact request for entry1
|
|
|
|
delReqTime := time.Now()
|
|
|
|
delReq := shared.DeletionRequest{
|
|
|
|
UserId: data.UserId("dkey"),
|
|
|
|
SendTime: delReqTime,
|
|
|
|
Messages: shared.MessageIdentifiers{Ids: []shared.MessageIdentifier{
|
|
|
|
{DeviceId: devId1, Date: entry1.EndTime},
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
reqBody, err = json.Marshal(delReq)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
2023-09-07 15:43:04 +02:00
|
|
|
addDeletionRequestHandler(httptest.NewRecorder(), req)
|
2022-09-22 06:46:46 +02:00
|
|
|
|
|
|
|
// Query again for device id 1 and get a single result
|
2022-11-26 21:10:18 +01:00
|
|
|
time.Sleep(10 * time.Millisecond)
|
2022-09-22 06:46:46 +02:00
|
|
|
w = httptest.NewRecorder()
|
|
|
|
searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiQueryHandler(w, searchReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &retrievedEntries))
|
2022-09-22 06:46:46 +02:00
|
|
|
if len(retrievedEntries) != 1 {
|
|
|
|
t.Fatalf("Expected to retrieve 1 entry, found %d", len(retrievedEntries))
|
|
|
|
}
|
|
|
|
dbEntry := retrievedEntries[0]
|
|
|
|
if dbEntry.DeviceId != devId1 {
|
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
|
|
|
if dbEntry.UserId != data.UserId("dkey") {
|
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-11-26 21:10:18 +01:00
|
|
|
if dbEntry.ReadCount != 1 {
|
2022-09-22 06:46:46 +02:00
|
|
|
t.Fatalf("db.ReadCount should have been 1, was %v", dbEntry.ReadCount)
|
|
|
|
}
|
|
|
|
decEntry, err := data.DecryptHistoryEntry("dkey", *dbEntry)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
if !data.EntryEquals(decEntry, entry2) {
|
|
|
|
t.Fatalf("DB data is different than input! \ndb =%#v\nentry=%#v", *dbEntry, entry2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query for user 2
|
|
|
|
w = httptest.NewRecorder()
|
|
|
|
searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev1+"&user_id="+otherUser, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiQueryHandler(w, searchReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &retrievedEntries))
|
2022-09-22 06:46:46 +02:00
|
|
|
if len(retrievedEntries) != 1 {
|
|
|
|
t.Fatalf("Expected to retrieve 1 entry, found %d", len(retrievedEntries))
|
|
|
|
}
|
|
|
|
dbEntry = retrievedEntries[0]
|
|
|
|
if dbEntry.DeviceId != otherDev1 {
|
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
|
|
|
if dbEntry.UserId != data.UserId("dOtherkey") {
|
|
|
|
t.Fatalf("Response contains an incorrect device ID: %#v", *dbEntry)
|
|
|
|
}
|
2022-11-26 21:10:18 +01:00
|
|
|
if dbEntry.ReadCount != 0 {
|
2022-09-22 06:46:46 +02:00
|
|
|
t.Fatalf("db.ReadCount should have been 1, was %v", dbEntry.ReadCount)
|
|
|
|
}
|
|
|
|
decEntry, err = data.DecryptHistoryEntry("dOtherkey", *dbEntry)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
if !data.EntryEquals(decEntry, entry3) {
|
|
|
|
t.Fatalf("DB data is different than input! \ndb =%#v\nentry=%#v", *dbEntry, entry3)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query for deletion requests
|
|
|
|
w = httptest.NewRecorder()
|
|
|
|
searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
getDeletionRequestsHandler(w, searchReq)
|
2022-09-22 06:46:46 +02:00
|
|
|
res = w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err = io.ReadAll(res.Body)
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, err)
|
2022-09-22 06:46:46 +02:00
|
|
|
var deletionRequests []*shared.DeletionRequest
|
2022-10-28 06:53:47 +02:00
|
|
|
testutils.Check(t, json.Unmarshal(respBody, &deletionRequests))
|
2022-09-22 06:46:46 +02:00
|
|
|
if len(deletionRequests) != 1 {
|
|
|
|
t.Fatalf("received %d deletion requests, expected only one", len(deletionRequests))
|
|
|
|
}
|
|
|
|
deletionRequest := deletionRequests[0]
|
|
|
|
expected := shared.DeletionRequest{
|
|
|
|
UserId: data.UserId("dkey"),
|
|
|
|
DestinationDeviceId: devId1,
|
|
|
|
SendTime: delReqTime,
|
|
|
|
ReadCount: 1,
|
|
|
|
Messages: shared.MessageIdentifiers{Ids: []shared.MessageIdentifier{
|
|
|
|
{DeviceId: devId1, Date: entry1.EndTime},
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
if diff := deep.Equal(*deletionRequest, expected); diff != nil {
|
|
|
|
t.Error(diff)
|
|
|
|
}
|
2022-11-27 07:40:43 +01:00
|
|
|
|
|
|
|
// Assert that we aren't leaking connections
|
|
|
|
assertNoLeakedConnections(t, GLOBAL_DB)
|
|
|
|
}
|
|
|
|
|
2022-11-27 07:53:14 +01:00
|
|
|
func TestHealthcheck(t *testing.T) {
|
|
|
|
w := httptest.NewRecorder()
|
2023-09-07 15:43:04 +02:00
|
|
|
healthCheckHandler(w, httptest.NewRequest(http.MethodGet, "/", nil))
|
2022-11-27 07:53:14 +01:00
|
|
|
if w.Code != 200 {
|
|
|
|
t.Fatalf("expected 200 resp code for healthCheckHandler")
|
|
|
|
}
|
|
|
|
res := w.Result()
|
|
|
|
defer res.Body.Close()
|
2022-11-27 20:59:06 +01:00
|
|
|
respBody, err := io.ReadAll(res.Body)
|
2022-11-27 07:53:14 +01:00
|
|
|
testutils.Check(t, err)
|
|
|
|
if string(respBody) != "OK" {
|
|
|
|
t.Fatalf("expected healthcheckHandler to return OK")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assert that we aren't leaking connections
|
|
|
|
assertNoLeakedConnections(t, GLOBAL_DB)
|
|
|
|
}
|
|
|
|
|
2022-12-12 04:42:51 +01:00
|
|
|
func TestLimitRegistrations(t *testing.T) {
|
|
|
|
// Set up
|
|
|
|
InitDB()
|
2022-12-12 05:31:50 +01:00
|
|
|
checkGormResult(GLOBAL_DB.Exec("DELETE FROM enc_history_entries"))
|
|
|
|
checkGormResult(GLOBAL_DB.Exec("DELETE FROM devices"))
|
2022-12-12 04:42:51 +01:00
|
|
|
defer testutils.BackupAndRestoreEnv("HISHTORY_MAX_NUM_USERS")()
|
|
|
|
os.Setenv("HISHTORY_MAX_NUM_USERS", "2")
|
|
|
|
|
|
|
|
// Register three devices across two users
|
|
|
|
deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user1"), nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-12-12 04:42:51 +01:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user1"), nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-12-12 04:42:51 +01:00
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user2"), nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-12-12 04:42:51 +01:00
|
|
|
|
|
|
|
// And this next one should fail since it is a new user
|
|
|
|
defer func() { _ = recover() }()
|
|
|
|
deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+uuid.Must(uuid.NewRandom()).String()+"&user_id="+data.UserId("user3"), nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-12-12 04:42:51 +01:00
|
|
|
t.Errorf("expected panic")
|
|
|
|
}
|
|
|
|
|
2022-12-18 06:27:00 +01:00
|
|
|
func TestCleanDatabaseNoErrors(t *testing.T) {
|
|
|
|
// Init
|
|
|
|
InitDB()
|
|
|
|
|
|
|
|
// Create a user and an entry
|
|
|
|
userId := data.UserId("dkey")
|
|
|
|
devId1 := uuid.Must(uuid.NewRandom()).String()
|
|
|
|
deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil)
|
2023-09-07 15:43:04 +02:00
|
|
|
apiRegisterHandler(httptest.NewRecorder(), deviceReq)
|
2022-12-18 06:27:00 +01:00
|
|
|
entry1 := testutils.MakeFakeHistoryEntry("ls ~/")
|
|
|
|
entry1.DeviceId = devId1
|
|
|
|
encEntry, err := data.EncryptHistoryEntry("dkey", entry1)
|
|
|
|
testutils.Check(t, err)
|
|
|
|
reqBody, err := json.Marshal([]shared.EncHistoryEntry{encEntry})
|
|
|
|
testutils.Check(t, err)
|
|
|
|
submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
2023-09-07 15:43:04 +02:00
|
|
|
apiSubmitHandler(httptest.NewRecorder(), submitReq)
|
2022-12-18 06:27:00 +01:00
|
|
|
|
|
|
|
// Call cleanDatabase and just check that there are no panics
|
|
|
|
testutils.Check(t, cleanDatabase(context.TODO()))
|
|
|
|
}
|
|
|
|
|
2022-11-27 07:40:43 +01:00
|
|
|
func assertNoLeakedConnections(t *testing.T, db *gorm.DB) {
|
|
|
|
sqlDB, err := db.DB()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
numConns := sqlDB.Stats().OpenConnections
|
|
|
|
if numConns > 1 {
|
|
|
|
t.Fatalf("expected DB to have not leak connections, actually have %d", numConns)
|
|
|
|
}
|
2022-09-22 06:46:46 +02:00
|
|
|
}
|