// Copyright © 2016 Asteris, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fetch_test

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"
	"testing"

	"github.com/asteris-llc/converge/helpers/fakerenderer"
	"github.com/asteris-llc/converge/resource"
	"github.com/asteris-llc/converge/resource/file/fetch"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
)

// TestFetchInterface tests that Fetch is properly implemented
func TestFetchInterface(t *testing.T) {
	t.Parallel()

	assert.Implements(t, (*resource.Task)(nil), new(fetch.Fetch))
}

// TestCheck tests the cases Check handles
func TestCheck(t *testing.T) {
	t.Parallel()

	t.Run("hash error", func(t *testing.T) {
		task := fetch.Fetch{
			Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
			Destination: "/tmp/converge.tar.gz",
			HashType:    "invalid",
			Hash:        "notarealhashbutstringnonetheless",
		}

		status, err := task.Check(context.Background(), fakerenderer.New())

		assert.EqualError(t, err, fmt.Sprintf("will not attempt file fetch: unsupported hashType %q", task.HashType))
		assert.True(t, status.HasChanges())
	})

	t.Run("fetch new file", func(t *testing.T) {
		t.Run("with checksum", func(t *testing.T) {
			task := fetch.Fetch{
				Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
				Destination: "/tmp/converge.tar.gz",
				HashType:    string(fetch.HashMD5),
				Hash:        "notarealhashbutstringnonetheless",
			}

			status, err := task.Check(context.Background(), fakerenderer.New())

			assert.NoError(t, err)
			assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
			assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
			assert.True(t, status.HasChanges())
		})

		t.Run("no checksum", func(t *testing.T) {
			task := fetch.Fetch{
				Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
				Destination: "/tmp/converge.tar.gz",
			}

			status, err := task.Check(context.Background(), fakerenderer.New())

			assert.NoError(t, err)
			assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
			assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
			assert.True(t, status.HasChanges())
		})
	})

	t.Run("context", func(t *testing.T) {
		task := fetch.Fetch{}
		ctx, cancel := context.WithCancel(context.Background())
		cancel()

		status, err := task.Check(ctx, fakerenderer.New())

		assert.EqualError(t, err, "context canceled")
		assert.Nil(t, status)
	})
}

// TestApply tests the cases Apply handles
func TestApply(t *testing.T) {
	t.Parallel()

	t.Run("hash error", func(t *testing.T) {
		task := fetch.Fetch{
			Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
			Destination: "/tmp/converge.tar.gz",
			HashType:    "invalid",
			Hash:        "notarealhashbutstringnonetheless",
		}
		defer os.Remove(task.Destination)

		status, err := task.Apply(context.Background())

		assert.EqualError(t, err, fmt.Sprintf("will not attempt file fetch: unsupported hashType %q", task.HashType))
		assert.True(t, status.HasChanges())
	})

	t.Run("source error", func(t *testing.T) {
		m := &MockDiff{}
		task := fetch.Fetch{
			Source:      ":test",
			Destination: "/tmp/fetch_test.txt",
			Force:       true,
		}
		defer os.Remove(task.Destination)

		stat := resource.NewStatus()
		stat.RaiseLevel(resource.StatusWillChange)
		m.On("DiffFile", nil, stat).Return(stat, nil)

		status, err := task.Apply(context.Background())

		assert.EqualError(t, err, fmt.Sprintf("could not parse source: parse %s: missing protocol scheme", task.Source))
		assert.False(t, status.HasChanges())
	})

	t.Run("failed to fetch", func(t *testing.T) {
		m := &MockDiff{}
		task := fetch.Fetch{
			Source:      "",
			Destination: "/tmp/fetch_test.txt",
			Force:       true,
		}
		defer os.Remove(task.Destination)

		stat := resource.NewStatus()
		m.On("DiffFile", nil, stat).Return(stat, nil)

		status, err := task.Apply(context.Background())

		assert.EqualError(t, err, "failed to fetch: source path must be a file")
		assert.False(t, status.HasChanges())
	})

	t.Run("with checksum", func(t *testing.T) {
		t.Run("file exists", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			hash, err := getHash(src.Name(), string(fetch.HashMD5))
			require.NoError(t, err)

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				HashType:    string(fetch.HashMD5),
				Hash:        hex.EncodeToString(hash.Sum(nil)),
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", nil, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "file exists")
			assert.False(t, status.HasChanges())
		})

		t.Run("force=true", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			hash, err := getHash(src.Name(), string(fetch.HashMD5))
			require.NoError(t, err)

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				HashType:    string(fetch.HashMD5),
				Hash:        "notarealhashbutstringnonetheless",
				Force:       true,
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", hash, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "fetched successfully")
			assert.Equal(t, hex.EncodeToString(hash.Sum(nil)), status.Diffs()["checksum"].Original())
			assert.Equal(t, task.Hash, status.Diffs()["checksum"].Current())
			assert.True(t, status.HasChanges())
		})

		t.Run("force=false", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			hash, err := getHash(src.Name(), string(fetch.HashMD5))
			require.NoError(t, err)

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				HashType:    string(fetch.HashMD5),
				Hash:        "notarealhashbutstringnonetheless",
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", hash, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.EqualError(t, err, "will not attempt fetch: checksum mismatch")
			assert.Contains(t, status.Messages(), "checksum mismatch, use the \"force\" option to replace")
			assert.True(t, status.HasChanges())
		})
	})

	t.Run("no checksum", func(t *testing.T) {
		t.Run("force=true", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				Force:       true,
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", nil, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "fetched successfully")
			assert.Equal(t, "<force fetch>", status.Diffs()["destination"].Original())
			assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
			assert.True(t, status.HasChanges())
		})

		t.Run("force=false", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", nil, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "file exists")
			assert.False(t, status.HasChanges())
		})
	})

	t.Run("fetch new file", func(t *testing.T) {
		t.Run("with checksum", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			hash, err := getHash(src.Name(), string(fetch.HashMD5))
			require.NoError(t, err)

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: "/tmp/fetch_test2.txt",
				HashType:    string(fetch.HashMD5),
				Hash:        hex.EncodeToString(hash.Sum(nil)),
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", hash, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "fetched successfully")
			assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
			assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
			assert.True(t, status.HasChanges())
		})

		t.Run("no checksum", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: "/tmp/fetch_test2.txt",
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", nil, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "fetched successfully")
			assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
			assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
			assert.True(t, status.HasChanges())
		})
	})

	t.Run("unarchive", func(t *testing.T) {
		t.Run("dest=file", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test2.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			m := &MockDiff{}
			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				Unarchive:   true,
			}
			defer os.Remove(task.Destination)

			stat := resource.NewStatus()
			m.On("DiffFile", nil, stat).Return(stat, nil)

			status, err := task.Apply(context.Background())

			assert.EqualError(t, err, fmt.Sprintf("invalid destination %q for unarchiving, must be directory", task.Destination))
			assert.True(t, status.HasChanges())
		})

		t.Run("dest=dir", func(t *testing.T) {
			t.Run("dest not exist", func(t *testing.T) {
				src, err := ioutil.TempFile("", "fetch_test.txt")
				require.NoError(t, err)
				defer os.Remove(src.Name())

				m := &MockDiff{}
				task := fetch.Fetch{
					Source:      src.Name(),
					Destination: "/tmp/fetch_test12345678",
					Unarchive:   true,
				}
				defer os.RemoveAll(task.Destination)

				stat := resource.NewStatus()
				m.On("DiffFile", nil, stat).Return(stat, nil)

				status, err := task.Apply(context.Background())

				assert.NoError(t, err)
				assert.Contains(t, status.Messages(), "fetched successfully")
				assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
				assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
				assert.True(t, status.HasChanges())
			})

			t.Run("dest exists", func(t *testing.T) {
				src, err := ioutil.TempFile("", "fetch_test.txt")
				require.NoError(t, err)
				defer os.Remove(src.Name())

				dest, err := ioutil.TempDir("", "fetch_test")
				require.NoError(t, err)
				defer os.Remove(dest)

				m := &MockDiff{}
				task := fetch.Fetch{
					Source:      src.Name(),
					Destination: dest,
					Unarchive:   true,
				}
				defer os.RemoveAll(task.Destination)

				stat := resource.NewStatus()
				m.On("DiffFile", nil, stat).Return(stat, nil)

				status, err := task.Apply(context.Background())

				assert.NoError(t, err)
				assert.Contains(t, status.Messages(), "fetched successfully")
				assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
				assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
				assert.True(t, status.HasChanges())
			})
		})
	})

	t.Run("context", func(t *testing.T) {
		task := fetch.Fetch{}
		ctx, cancel := context.WithCancel(context.Background())
		cancel()

		status, err := task.Apply(ctx)

		assert.EqualError(t, err, "context canceled")
		assert.Nil(t, status)
	})
}

// TestDiffFile tests DiffFile
func TestDiffFile(t *testing.T) {
	t.Parallel()

	t.Run("fetch new file", func(t *testing.T) {
		t.Run("with checksum", func(t *testing.T) {
			task := fetch.Fetch{
				Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
				Destination: "/tmp/converge.tar.gz",
				HashType:    string(fetch.HashMD5),
				Hash:        "notarealhashbutstringnonetheless",
			}

			status, err := task.DiffFile(resource.NewStatus(), nil)

			assert.NoError(t, err)
			assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
			assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
			assert.True(t, status.HasChanges())
		})

		t.Run("no checksum", func(t *testing.T) {
			task := fetch.Fetch{
				Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
				Destination: "/tmp/converge.tar.gz",
			}

			status, err := task.DiffFile(resource.NewStatus(), nil)

			assert.NoError(t, err)
			assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
			assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
			assert.True(t, status.HasChanges())
		})
	})

	t.Run("unarchive", func(t *testing.T) {
		t.Run("dest=file", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				Unarchive:   true,
			}

			status, err := task.DiffFile(resource.NewStatus(), nil)

			assert.EqualError(t, err, fmt.Sprintf("invalid destination %q for unarchiving, must be directory", task.Destination))
			assert.True(t, status.HasChanges())
		})

		t.Run("dest=dir", func(t *testing.T) {
			t.Run("dest not exist", func(t *testing.T) {
				src, err := ioutil.TempFile("", "fetch_test.txt")
				require.NoError(t, err)
				defer os.Remove(src.Name())

				task := fetch.Fetch{
					Source:      src.Name(),
					Destination: "/tmp/fetch_test12345678",
					Unarchive:   true,
				}

				status, err := task.DiffFile(resource.NewStatus(), nil)

				assert.NoError(t, err)
				assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
				assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
				assert.True(t, status.HasChanges())
			})

			t.Run("dest exists", func(t *testing.T) {
				src, err := ioutil.TempFile("", "fetch_test.txt")
				require.NoError(t, err)
				defer os.Remove(src.Name())

				dest, err := ioutil.TempDir("", "fetch_test")
				require.NoError(t, err)
				defer os.Remove(dest)

				task := fetch.Fetch{
					Source:      src.Name(),
					Destination: dest,
					Unarchive:   true,
				}

				status, err := task.DiffFile(resource.NewStatus(), nil)
				fmt.Printf("fetch=%v\n", task)
				fmt.Printf("status=%v\n", status)

				assert.NoError(t, err)
				assert.Equal(t, "<absent>", status.Diffs()["destination"].Original())
				assert.Equal(t, task.Destination, status.Diffs()["destination"].Current())
				assert.True(t, status.HasChanges())
			})
		})
	})

	t.Run("destination error-directory", func(t *testing.T) {
		t.Run("with checksum", func(t *testing.T) {
			task := fetch.Fetch{
				Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
				Destination: "/tmp",
				HashType:    string(fetch.HashMD5),
				Hash:        "notarealhashbutstringnonetheless",
			}

			status, err := task.DiffFile(resource.NewStatus(), nil)

			assert.EqualError(t, err, fmt.Sprintf("invalid destination \"%s\", cannot be directory", task.Destination))
			assert.True(t, status.HasChanges())
		})

		t.Run("no checksum", func(t *testing.T) {
			task := fetch.Fetch{
				Source:      "https://github.com/asteris-llc/converge/releases/download/0.2.0/converge_0.2.0_darwin_amd64.tar.gz",
				Destination: "/tmp",
			}

			status, err := task.DiffFile(resource.NewStatus(), nil)

			assert.EqualError(t, err, fmt.Sprintf("invalid destination \"%s\", cannot be directory", task.Destination))
			assert.True(t, status.HasChanges())
		})
	})

	t.Run("checksums differ", func(t *testing.T) {
		t.Run("force=true", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			hash, err := getHash(src.Name(), string(fetch.HashMD5))
			require.NoError(t, err)

			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				HashType:    string(fetch.HashMD5),
				Hash:        "notarealhashbutstringnonetheless",
				Force:       true,
			}

			status, err := task.DiffFile(resource.NewStatus(), hash)

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "checksum mismatch")
			assert.Equal(t, hex.EncodeToString(hash.Sum(nil)), status.Diffs()["checksum"].Original())
			assert.Equal(t, task.Hash, status.Diffs()["checksum"].Current())
			assert.True(t, status.HasChanges())
		})

		t.Run("force=false", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			hash, err := getHash(src.Name(), string(fetch.HashMD5))
			require.NoError(t, err)

			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				HashType:    string(fetch.HashMD5),
				Hash:        "notarealhashbutstringnonetheless",
			}

			status, err := task.DiffFile(resource.NewStatus(), hash)

			assert.EqualError(t, err, "will not attempt fetch: checksum mismatch")
			assert.Contains(t, status.Messages(), "checksum mismatch, use the \"force\" option to replace")
			assert.True(t, status.HasChanges())
		})
	})

	t.Run("file exists", func(t *testing.T) {
		t.Run("force=true", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				Force:       true,
			}

			status, err := task.DiffFile(resource.NewStatus(), nil)

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "file exists, will fetch due to \"force\"")
			assert.True(t, status.HasChanges())
		})

		t.Run("force=false", func(t *testing.T) {
			src, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(src.Name())

			dest, err := ioutil.TempFile("", "fetch_test.txt")
			require.NoError(t, err)
			defer os.Remove(dest.Name())

			hash, err := getHash(src.Name(), string(fetch.HashMD5))
			require.NoError(t, err)

			task := fetch.Fetch{
				Source:      src.Name(),
				Destination: dest.Name(),
				HashType:    string(fetch.HashMD5),
				Hash:        hex.EncodeToString(hash.Sum(nil)),
			}

			status, err := task.DiffFile(resource.NewStatus(), hash)

			assert.NoError(t, err)
			assert.Contains(t, status.Messages(), "file exists")
			assert.False(t, status.HasChanges())
		})
	})
}

func getHash(path, hashType string) (hash.Hash, error) {
	var h hash.Hash
	switch hashType {
	case string(fetch.HashMD5):
		h = md5.New()
	case string(fetch.HashSHA1):
		h = sha1.New()
	case string(fetch.HashSHA256):
		h = sha256.New()
	case string(fetch.HashSHA512):
		h = sha512.New()
	default:
		err := fmt.Errorf("unsupported hashType %q", hashType)
		return nil, err
	}

	f, err := os.Open(path)
	if err != nil {
		err = fmt.Errorf("Failed to open file for checksum: %s", err)
		return nil, err
	}
	defer f.Close()
	_, err = io.Copy(h, f)
	return h, err
}

// MockDiff is a mock implementation for file diffs
type MockDiff struct {
	mock.Mock
}

// DiffFile mocks the fetch DiffFile
func (m *MockDiff) DiffFile(r resource.Status, hsh hash.Hash) (*resource.Status, error) {
	args := m.Called(r, hsh)
	return args.Get(0).(*resource.Status), args.Error(1)
}
