123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- // Copyright (c) 2016-2022 Uber Technologies, Inc.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- package zap
- import (
- "errors"
- "io"
- "io/fs"
- "net/url"
- "os"
- "path/filepath"
- "testing"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "go.uber.org/multierr"
- "go.uber.org/zap/zapcore"
- )
- func TestOpenNoPaths(t *testing.T) {
- ws, cleanup, err := Open()
- defer cleanup()
- assert.NoError(t, err, "Expected opening no paths to succeed.")
- assert.Equal(
- t,
- zapcore.AddSync(io.Discard),
- ws,
- "Expected opening no paths to return a no-op WriteSyncer.",
- )
- }
- func TestOpen(t *testing.T) {
- tempName := filepath.Join(t.TempDir(), "test.log")
- assert.False(t, fileExists(tempName))
- require.True(t, filepath.IsAbs(tempName), "Expected absolute temp file path.")
- tests := []struct {
- msg string
- paths []string
- }{
- {
- msg: "stdout",
- paths: []string{"stdout"},
- },
- {
- msg: "stderr",
- paths: []string{"stderr"},
- },
- {
- msg: "temp file path only",
- paths: []string{tempName},
- },
- {
- msg: "temp file file scheme",
- paths: []string{"file://" + tempName},
- },
- {
- msg: "temp file with file scheme and host localhost",
- paths: []string{"file://localhost" + tempName},
- },
- }
- for _, tt := range tests {
- t.Run(tt.msg, func(t *testing.T) {
- _, cleanup, err := Open(tt.paths...)
- if err == nil {
- defer cleanup()
- }
- assert.NoError(t, err, "Unexpected error opening paths %v.", tt.paths)
- })
- }
- assert.True(t, fileExists(tempName))
- }
- func TestOpenPathsNotFound(t *testing.T) {
- tempName := filepath.Join(t.TempDir(), "test.log")
- tests := []struct {
- msg string
- paths []string
- wantNotFoundPaths []string
- }{
- {
- msg: "missing path",
- paths: []string{"/foo/bar/baz"},
- wantNotFoundPaths: []string{"/foo/bar/baz"},
- },
- {
- msg: "missing file scheme url with host localhost",
- paths: []string{"file://localhost/foo/bar/baz"},
- wantNotFoundPaths: []string{"/foo/bar/baz"},
- },
- {
- msg: "multiple paths",
- paths: []string{"stdout", "/foo/bar/baz", tempName, "file:///baz/quux"},
- wantNotFoundPaths: []string{
- "/foo/bar/baz",
- "/baz/quux",
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.msg, func(t *testing.T) {
- _, cleanup, err := Open(tt.paths...)
- if !assert.Error(t, err, "Open must fail.") {
- cleanup()
- return
- }
- errs := multierr.Errors(err)
- require.Len(t, errs, len(tt.wantNotFoundPaths))
- for i, err := range errs {
- assert.ErrorIs(t, err, fs.ErrNotExist)
- assert.ErrorContains(t, err, tt.wantNotFoundPaths[i], "missing path in error")
- }
- })
- }
- }
- func TestOpenRelativePath(t *testing.T) {
- const name = "test-relative-path.txt"
- require.False(t, fileExists(name), "Test file already exists.")
- s, cleanup, err := Open(name)
- require.NoError(t, err, "Open failed.")
- defer func() {
- err := os.Remove(name)
- if !t.Failed() {
- // If the test has already failed, we probably didn't create this file.
- require.NoError(t, err, "Deleting test file failed.")
- }
- }()
- defer cleanup()
- _, err = s.Write([]byte("test"))
- assert.NoError(t, err, "Write failed.")
- assert.True(t, fileExists(name), "Didn't create file for relative path.")
- }
- func TestOpenFails(t *testing.T) {
- tests := []struct {
- paths []string
- }{
- {paths: []string{"./non-existent-dir/file"}}, // directory doesn't exist
- {paths: []string{"stdout", "./non-existent-dir/file"}}, // directory doesn't exist
- {paths: []string{"://foo.log"}}, // invalid URL, scheme can't begin with colon
- {paths: []string{"mem://somewhere"}}, // scheme not registered
- }
- for _, tt := range tests {
- _, cleanup, err := Open(tt.paths...)
- require.Nil(t, cleanup, "Cleanup function should never be nil")
- assert.Error(t, err, "Open with invalid URL should fail.")
- }
- }
- func TestOpenOtherErrors(t *testing.T) {
- tempName := filepath.Join(t.TempDir(), "test.log")
- tests := []struct {
- msg string
- paths []string
- wantErr string
- }{
- {
- msg: "file with unexpected host",
- paths: []string{"file://host01.test.com" + tempName},
- wantErr: "empty or use localhost",
- },
- {
- msg: "file with user on localhost",
- paths: []string{"file://rms@localhost" + tempName},
- wantErr: "user and password not allowed",
- },
- {
- msg: "file url with fragment",
- paths: []string{"file://localhost" + tempName + "#foo"},
- wantErr: "fragments not allowed",
- },
- {
- msg: "file url with query",
- paths: []string{"file://localhost" + tempName + "?foo=bar"},
- wantErr: "query parameters not allowed",
- },
- {
- msg: "file with port",
- paths: []string{"file://localhost:8080" + tempName},
- wantErr: "ports not allowed",
- },
- }
- for _, tt := range tests {
- t.Run(tt.msg, func(t *testing.T) {
- _, cleanup, err := Open(tt.paths...)
- if !assert.Error(t, err, "Open must fail.") {
- cleanup()
- return
- }
- assert.ErrorContains(t, err, tt.wantErr, "Unexpected error opening paths %v.", tt.paths)
- })
- }
- }
- type testWriter struct {
- expected string
- t testing.TB
- }
- func (w *testWriter) Write(actual []byte) (int, error) {
- assert.Equal(w.t, []byte(w.expected), actual, "Unexpected write error.")
- return len(actual), nil
- }
- func (w *testWriter) Sync() error {
- return nil
- }
- func TestOpenWithErroringSinkFactory(t *testing.T) {
- stubSinkRegistry(t)
- msg := "expected factory error"
- factory := func(_ *url.URL) (Sink, error) {
- return nil, errors.New(msg)
- }
- assert.NoError(t, RegisterSink("test", factory), "Failed to register sink factory.")
- _, _, err := Open("test://some/path")
- assert.ErrorContains(t, err, msg)
- }
- func TestCombineWriteSyncers(t *testing.T) {
- tw := &testWriter{"test", t}
- w := CombineWriteSyncers(tw)
- _, err := w.Write([]byte("test"))
- assert.NoError(t, err, "Unexpected write error.")
- }
- func fileExists(name string) bool {
- if _, err := os.Stat(name); os.IsNotExist(err) {
- return false
- }
- return true
- }
|