Skip to content

Commit

Permalink
add json printer to support the json output format
Browse files Browse the repository at this point in the history
  • Loading branch information
chunglu-chou committed Aug 11, 2022
1 parent 291703a commit 0dabf06
Show file tree
Hide file tree
Showing 3 changed files with 452 additions and 13 deletions.
353 changes: 340 additions & 13 deletions cmd/status/cmdstatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package status
import (
"bytes"
"context"
"encoding/json"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -53,6 +54,23 @@ metadata:
}
)

type fakePoller struct {
events []pollevent.Event
}

func (f *fakePoller) Poll(ctx context.Context, _ object.ObjMetadataSet,
_ polling.PollOptions) <-chan pollevent.Event {
eventChannel := make(chan pollevent.Event)
go func() {
defer close(eventChannel)
for _, e := range f.events {
eventChannel <- e
}
<-ctx.Done()
}()
return eventChannel
}

func TestCommand(t *testing.T) {
testCases := map[string]struct {
pollUntil string
Expand Down Expand Up @@ -217,6 +235,261 @@ foo/deployment.apps/default/foo is InProgress: inProgress
},
}

jsonTestCases := map[string]struct {
pollUntil string
printer string
timeout time.Duration
input string
inventory object.ObjMetadataSet
events []pollevent.Event
expectedErrMsg string
expectedOutput []map[string]interface{}
}{
"wait for all known json": {
pollUntil: "known",
printer: "json",
input: inventoryTemplate,
inventory: object.ObjMetadataSet{
depObject,
stsObject,
},
events: []pollevent.Event{
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: depObject,
Status: status.InProgressStatus,
Message: "inProgress",
},
},
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: stsObject,
Status: status.CurrentStatus,
Message: "current",
},
},
},
expectedOutput: []map[string]interface{}{
{
"group": "apps",
"kind": "Deployment",
"namespace": "default",
"name": "foo",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "InProgress",
"message": "inProgress",
},
{
"group": "apps",
"kind": "StatefulSet",
"namespace": "default",
"name": "bar",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "Current",
"message": "current",
},
},
},
"wait for all current json": {
pollUntil: "current",
printer: "json",
input: inventoryTemplate,
inventory: object.ObjMetadataSet{
depObject,
stsObject,
},
events: []pollevent.Event{
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: depObject,
Status: status.InProgressStatus,
Message: "inProgress",
},
},
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: stsObject,
Status: status.InProgressStatus,
Message: "inProgress",
},
},
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: stsObject,
Status: status.CurrentStatus,
Message: "current",
},
},
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: depObject,
Status: status.CurrentStatus,
Message: "current",
},
},
},
expectedOutput: []map[string]interface{}{
{
"group": "apps",
"kind": "Deployment",
"namespace": "default",
"name": "foo",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "InProgress",
"message": "inProgress",
},
{
"group": "apps",
"kind": "StatefulSet",
"namespace": "default",
"name": "bar",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "InProgress",
"message": "inProgress",
},
{
"group": "apps",
"kind": "StatefulSet",
"namespace": "default",
"name": "bar",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "Current",
"message": "current",
},
{
"group": "apps",
"kind": "Deployment",
"namespace": "default",
"name": "foo",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "Current",
"message": "current",
},
},
},
"wait for all deleted json": {
pollUntil: "deleted",
printer: "json",
input: inventoryTemplate,
inventory: object.ObjMetadataSet{
depObject,
stsObject,
},
events: []pollevent.Event{
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: stsObject,
Status: status.NotFoundStatus,
Message: "notFound",
},
},
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: depObject,
Status: status.NotFoundStatus,
Message: "notFound",
},
},
},
expectedOutput: []map[string]interface{}{
{
"group": "apps",
"kind": "StatefulSet",
"namespace": "default",
"name": "bar",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "NotFound",
"message": "notFound",
},
{
"group": "apps",
"kind": "Deployment",
"namespace": "default",
"name": "foo",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "NotFound",
"message": "notFound",
},
},
},
"forever with timeout json": {
pollUntil: "forever",
printer: "json",
timeout: 2 * time.Second,
input: inventoryTemplate,
inventory: object.ObjMetadataSet{
depObject,
stsObject,
},
events: []pollevent.Event{
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: stsObject,
Status: status.InProgressStatus,
Message: "inProgress",
},
},
{
Type: pollevent.ResourceUpdateEvent,
Resource: &pollevent.ResourceStatus{
Identifier: depObject,
Status: status.InProgressStatus,
Message: "inProgress",
},
},
},
expectedOutput: []map[string]interface{}{
{
"group": "apps",
"kind": "StatefulSet",
"namespace": "default",
"name": "bar",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "InProgress",
"message": "inProgress",
},
{
"group": "apps",
"kind": "Deployment",
"namespace": "default",
"name": "foo",
"timestamp": "",
"type": "status",
"inventory-name": "foo",
"status": "InProgress",
"message": "inProgress",
},
},
},
}

for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
tf := cmdtesting.NewTestFactory().WithNamespace("namespace")
Expand Down Expand Up @@ -258,21 +531,75 @@ foo/deployment.apps/default/foo is InProgress: inProgress
assert.Equal(t, strings.TrimSpace(buf.String()), strings.TrimSpace(tc.expectedOutput))
})
}
}

type fakePoller struct {
events []pollevent.Event
for tn, tc := range jsonTestCases {
t.Run(tn, func(t *testing.T) {
tf := cmdtesting.NewTestFactory().WithNamespace("namespace")
defer tf.Cleanup()

loader := manifestreader.NewFakeLoader(tf, tc.inventory)
runner := &Runner{
factory: tf,
invFactory: inventory.FakeClientFactory(tc.inventory),
loader: loader,
pollerFactoryFunc: func(c cmdutil.Factory) (poller.Poller, error) {
return &fakePoller{tc.events}, nil
},

pollUntil: tc.pollUntil,
output: tc.printer,
timeout: tc.timeout,
}

cmd := &cobra.Command{
RunE: runner.runE,
}
cmd.SetIn(strings.NewReader(tc.input))
var buf bytes.Buffer
cmd.SetOut(&buf)
cmd.SetArgs([]string{})

err := cmd.Execute()
if tc.expectedErrMsg != "" {
if !assert.Error(t, err) {
t.FailNow()
}
assert.Contains(t, err.Error(), tc.expectedErrMsg)
return
}

assert.NoError(t, err)
actual := strings.Split(buf.String(), "\n")
assertOutput(t, tc.expectedOutput, actual)
})
}
}

func (f *fakePoller) Poll(ctx context.Context, _ object.ObjMetadataSet,
_ polling.PollOptions) <-chan pollevent.Event {
eventChannel := make(chan pollevent.Event)
go func() {
defer close(eventChannel)
for _, e := range f.events {
eventChannel <- e
// nolint:unparam
func assertOutput(t *testing.T, expectedOutput []map[string]interface{}, actual []string) bool {
for i, expectedMap := range expectedOutput {
if len(expectedMap) == 0 {
return assert.Empty(t, actual[i])
}
<-ctx.Done()
}()
return eventChannel

var m map[string]interface{}
err := json.Unmarshal([]byte(actual[i]), &m)
if !assert.NoError(t, err) {
return false
}

if _, found := expectedMap["timestamp"]; found {
if _, ok := m["timestamp"]; ok {
delete(expectedMap, "timestamp")
delete(m, "timestamp")
} else {
t.Error("expected to find key 'timestamp', but didn't")
return false
}
}
if !assert.Equal(t, expectedMap, m) {
return false
}
}
return true
}
Loading

0 comments on commit 0dabf06

Please sign in to comment.