package database import ( "database/sql" "encoding/json" "fmt" "io/ioutil" "os" "testing" "github.com/upper/db/v4" ) type locationInfo struct { asn uint countryCode string ip string networkName string resolverIP string } func (lp *locationInfo) ProbeASN() uint { return lp.asn } func (lp *locationInfo) ProbeASNString() string { return fmt.Sprintf("AS%d", lp.asn) } func (lp *locationInfo) ProbeCC() string { return lp.countryCode } func (lp *locationInfo) ProbeIP() string { return lp.ip } func (lp *locationInfo) ProbeNetworkName() string { return lp.networkName } func (lp *locationInfo) ResolverIP() string { return lp.resolverIP } func TestNewDatabase(t *testing.T) { t.Run("with empty path", func(t *testing.T) { dbpath := "" database, err := Open(dbpath) if database != nil { t.Fatal("unexpected database instance") } if err.Error() != "Expecting file:// connection scheme." { t.Fatal(err) } }) t.Run("with valid path", func(t *testing.T) { tmpfile, err := ioutil.TempFile("", "dbtest") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) _, err = Open(tmpfile.Name()) if err != nil { t.Fatal(err) } }) } func TestMeasurementWorkflow(t *testing.T) { tmpfile, err := ioutil.TempFile("", "dbtest") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) tmpdir, err := ioutil.TempDir("", "oonitest") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) database, err := Open(tmpfile.Name()) if err != nil { t.Fatal(err) } location := locationInfo{ asn: 0, countryCode: "IT", networkName: "Unknown", } sess := database.Session() network, err := database.CreateNetwork(&location) if err != nil { t.Fatal(err) } result, err := database.CreateResult(tmpdir, "websites", network.ID) if err != nil { t.Fatal(err) } reportID := sql.NullString{String: "", Valid: false} testName := "antani" resultID := result.ID msmtFilePath := tmpdir urlID := sql.NullInt64{Int64: 0, Valid: false} m1, err := database.CreateMeasurement(reportID, testName, msmtFilePath, 0, resultID, urlID) if err != nil { t.Fatal(err) } m1.IsUploaded = true m1.IsAnomaly = sql.NullBool{Valid: true, Bool: false} err = sess.Collection("measurements").Find("measurement_id", m1.ID).Update(m1) if err != nil { t.Fatal(err) } m2, err := database.CreateMeasurement(reportID, testName, msmtFilePath, 0, resultID, urlID) if err != nil { t.Fatal(err) } m2.IsUploaded = false m2.IsAnomaly = sql.NullBool{Valid: true, Bool: true} err = sess.Collection("measurements").Find("measurement_id", m2.ID).Update(m2) if err != nil { t.Fatal(err) } if m2.ResultID != m1.ResultID { t.Error("result_id mismatch") } err = database.UpdateUploadedStatus(result) if err != nil { t.Fatal(err) } result.Finished(sess) var r Result err = sess.Collection("measurements").Find("result_id", result.ID).One(&r) if err != nil { t.Fatal(err) } if r.IsUploaded == true { t.Error("result should be marked as not uploaded") } done, incomplete, err := database.ListResults() if err != nil { t.Fatal(err) } if len(incomplete) != 0 { t.Error("there should be 0 incomplete result") } if len(done) != 1 { t.Error("there should be 1 done result") } if done[0].TotalCount != 2 { t.Error("there should be a total of 2 measurements in the result") } if done[0].AnomalyCount != 1 { t.Error("there should be a total of 1 anomalies in the result") } msmts, err := database.ListMeasurements(resultID) if err != nil { t.Fatal(err) } if msmts[0].Network.NetworkType != "wifi" { t.Error("network_type should be wifi") } } func TestDeleteResult(t *testing.T) { tmpfile, err := ioutil.TempFile("", "dbtest") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) tmpdir, err := ioutil.TempDir("", "oonitest") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) database, err := Open(tmpfile.Name()) if err != nil { t.Fatal(err) } location := locationInfo{ asn: 0, countryCode: "IT", networkName: "Unknown", } sess := database.Session() network, err := database.CreateNetwork(&location) if err != nil { t.Fatal(err) } result, err := database.CreateResult(tmpdir, "websites", network.ID) if err != nil { t.Fatal(err) } reportID := sql.NullString{String: "", Valid: false} testName := "antani" resultID := result.ID msmtFilePath := tmpdir urlID := sql.NullInt64{Int64: 0, Valid: false} m1, err := database.CreateMeasurement(reportID, testName, msmtFilePath, 0, resultID, urlID) if err != nil { t.Fatal(err) } var m2 Measurement err = sess.Collection("measurements").Find("measurement_id", m1.ID).One(&m2) if err != nil { t.Fatal(err) } if m2.ResultID != m1.ResultID { t.Error("result_id mismatch") } err = database.DeleteResult(resultID) if err != nil { t.Fatal(err) } totalResults, err := sess.Collection("results").Find().Count() if err != nil { t.Fatal(err) } totalMeasurements, err := sess.Collection("measurements").Find().Count() if err != nil { t.Fatal(err) } if totalResults != 0 { t.Fatal("results should be zero") } if totalMeasurements != 0 { t.Fatal("measurements should be zero") } err = database.DeleteResult(20) if err != db.ErrNoMoreRows { t.Fatal(err) } } func TestNetworkCreate(t *testing.T) { tmpfile, err := ioutil.TempFile("", "dbtest") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) database, err := Open(tmpfile.Name()) if err != nil { t.Fatal(err) } l1 := locationInfo{ asn: 2, countryCode: "IT", networkName: "Antaninet", } l2 := locationInfo{ asn: 3, countryCode: "IT", networkName: "Fufnet", } _, err = database.CreateNetwork(&l1) if err != nil { t.Fatal(err) } _, err = database.CreateNetwork(&l2) if err != nil { t.Fatal(err) } } func TestURLCreation(t *testing.T) { tmpfile, err := ioutil.TempFile("", "dbtest") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) database, err := Open(tmpfile.Name()) if err != nil { t.Fatal(err) } newID1, err := database.CreateOrUpdateURL("https://google.com", "GMB", "XX") if err != nil { t.Fatal(err) } newID2, err := database.CreateOrUpdateURL("https://google.com", "SRCH", "XX") if err != nil { t.Fatal(err) } newID3, err := database.CreateOrUpdateURL("https://facebook.com", "GRP", "XX") if err != nil { t.Fatal(err) } newID4, err := database.CreateOrUpdateURL("https://facebook.com", "GMP", "XX") if err != nil { t.Fatal(err) } newID5, err := database.CreateOrUpdateURL("https://google.com", "SRCH", "XX") if err != nil { t.Fatal(err) } if newID2 != newID1 { t.Error("inserting the same URL with different category code should produce the same result") } if newID3 == newID1 { t.Error("inserting different URL should produce different ids") } if newID4 != newID3 { t.Error("inserting the same URL with different category code should produce the same result") } if newID5 != newID1 { t.Error("the ID of google should still be the same") } } func TestPerformanceTestKeys(t *testing.T) { var tk PerformanceTestKeys ndtS := "{\"download\":100.0,\"upload\":20.0,\"ping\":2.2}" dashS := "{\"median_bitrate\":102.0}" if err := json.Unmarshal([]byte(ndtS), &tk); err != nil { t.Fatal("failed to parse ndtS") } if err := json.Unmarshal([]byte(dashS), &tk); err != nil { t.Fatal("failed to parse dashS") } if tk.Bitrate != 102.0 { t.Fatalf("error Bitrate %f", tk.Bitrate) } if tk.Download != 100.0 { t.Fatalf("error Download %f", tk.Download) } } func TestGetMeasurementJSON(t *testing.T) { tmpfile, err := ioutil.TempFile("", "dbtest") if err != nil { t.Fatal(err) } defer os.Remove(tmpfile.Name()) tmpdir, err := ioutil.TempDir("", "oonitest") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) database, err := Open(tmpfile.Name()) if err != nil { t.Fatal(err) } sess := database.Session() location := locationInfo{ asn: 0, countryCode: "IT", networkName: "Unknown", } network, err := database.CreateNetwork(&location) if err != nil { t.Fatal(err) } result, err := database.CreateResult(tmpdir, "websites", network.ID) if err != nil { t.Fatal(err) } reportID := sql.NullString{String: "20210111T085144Z_ndt_RU_3216_n1_qMVnP0PTX7ObUSmD", Valid: true} testName := "antani" resultID := result.ID msmtFilePath := tmpdir urlID := sql.NullInt64{Int64: 0, Valid: false} msmt, err := database.CreateMeasurement(reportID, testName, msmtFilePath, 0, resultID, urlID) if err != nil { t.Fatal(err) } msmt.IsUploaded = true err = sess.Collection("measurements").Find("measurement_id", msmt.ID).Update(msmt) if err != nil { t.Fatal(err) } tk, err := database.GetMeasurementJSON(msmt.ID) if err != nil { t.Fatal(err) } if tk["probe_asn"] != "AS3216" { t.Error("inconsistent measurement downloaded") } }