package main import ( "bytes" "encoding/json" "errors" "io" "io/fs" "net/http" "net/http/httptest" "os" "path/filepath" "testing" ) func mkDirForTest(t *testing.T, fp string) { err := os.MkdirAll(fp, os.ModePerm) if err != nil { t.Fatal(err) } } func cpFileForTest(t *testing.T, src string, dst string) { var srcfd *os.File var dstfd *os.File var err error var srcinfo os.FileInfo srcfd, err = os.Open(src) if err != nil { if errors.Is(err, fs.ErrNotExist) { return } else { t.Fatal(err) } } // if err != nil { // t.Fatal(err) // } defer srcfd.Close() dstfd, err = os.Create(dst) if err != nil { t.Fatal(err) } defer dstfd.Close() _, err = io.Copy(dstfd, srcfd) if err != nil { t.Fatal(err) } srcinfo, err = os.Stat(src) if err != nil { t.Fatal(err) } err = os.Chmod(dst, srcinfo.Mode()) if err != nil { t.Fatal(err) } } func writeForTest(t *testing.T, fp string, data []byte) { err := os.WriteFile(fp, data, 0666) if err != nil { t.Fatal(err) } } func IsDirEmpty(t *testing.T, name string) bool { f, err := os.Open(name) if err != nil { t.Fatal(err) } defer f.Close() // read in ONLY one file _, err = f.Readdir(1) // and if the file is EOF... well, the dir is empty. return err == io.EOF } func doesFileExist(name string) bool { _, err := os.ReadFile(name) // defer fp.Close() return err == nil } // Derived from here (https://stackoverflow.com/a/55300382) func walkMatch(t *testing.T, root, pattern string) []string { var matches []string err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if matched, err := filepath.Match(pattern, filepath.Base(path)); err != nil { return err } else if matched { matches = append(matches, path) } return nil }) if err != nil { t.Fatal(err) } return matches } // func doesFileExist(name string) bool { // _, err := os.Stat(name) // return !errors.Is(err, fs.ErrNotExist) // } func removeFileForTest(t *testing.T, name string) { err := os.Remove(name) if err != nil { t.Fatal(err) } } func writeJsonForTest(t *testing.T, data map[string]any, fp string) { jsonString, _ := json.Marshal(data) err := os.WriteFile(fp, jsonString, os.ModePerm) if err != nil { t.Fatal(err) } } func isFileEmpty(t *testing.T, name string) bool { fd, err := os.Open(name) if err != nil { t.Fatal(err) } defer fd.Close() finfo, err := fd.Stat() if err != nil { t.Fatal(err) } return finfo.Size() < 1 } // Derived from here (https://stackoverflow.com/a/73411967) func areFilesTheSame(t *testing.T, fp_1 string, fp_2 string) bool { chunkSize := 4 * 1024 // shortcuts: check file metadata finfo_1, err := os.Stat(fp_1) if err != nil { t.Fatal(err) } finfo_2, err := os.Stat(fp_2) if err != nil { t.Fatal(err) } // are inputs are literally the same file? if os.SameFile(finfo_1, finfo_2) { return true } // do inputs at least have the same size? if finfo_1.Size() != finfo_2.Size() { return false } // long way: compare contents fd_1, err := os.Open(fp_1) if err != nil { t.Fatal(err) } defer fd_1.Close() fd_2, err := os.Open(fp_2) if err != nil { t.Fatal(err) } defer fd_2.Close() bfd_1 := make([]byte, chunkSize) bfd_2 := make([]byte, chunkSize) for { n1, err1 := io.ReadFull(fd_1, bfd_1) n2, err2 := io.ReadFull(fd_2, bfd_2) // https://pkg.go.dev/io#Reader // > Callers should always process the n > 0 bytes returned // > before considering the error err. Doing so correctly // > handles I/O errors that happen after reading some bytes // > and also both of the allowed EOF behaviors. if !bytes.Equal(bfd_1[:n1], bfd_2[:n2]) { return false } if (err1 == io.EOF && err2 == io.EOF) || (err1 == io.ErrUnexpectedEOF && err2 == io.ErrUnexpectedEOF) { return true } // some other error, like a dropped network connection or a bad transfer if err1 != nil { t.Fatal(err1) } if err2 != nil { t.Fatal(err2) } } } func readTestFile(t *testing.T, fp string) []byte { f, err := os.ReadFile(fp) if err != nil { t.Fatal(err) } return f } // func writeTestConfFile(t *testing.T, rulesPath string, logPath string) { // writeJsonForTest(t, p, ) // } // Integration tests func newTestApp(t *testing.T, cfg *Config, lfg *LogFile) *Application { return &Application{ Config: cfg, Log: lfg, } } type testServer struct { *httptest.Server } func newTestServer(t *testing.T, h http.Handler) *testServer { ts := httptest.NewServer(h) return &testServer{ts} } func (ts *testServer) get(t *testing.T, urlPath string) (int, http.Header, []byte) { rs, err := ts.Client().Get(ts.URL + urlPath) if err != nil { t.Fatal(err) } defer rs.Body.Close() body, err := io.ReadAll(rs.Body) if err != nil { t.Fatal(err) } body = bytes.TrimSpace(body) return rs.StatusCode, rs.Header, body }