github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/cmd/bacalhau/describe_test.go (about)

     1  //go:build unit || !integration
     2  
     3  package bacalhau
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/filecoin-project/bacalhau/pkg/bacerrors"
    11  	"github.com/filecoin-project/bacalhau/pkg/model"
    12  	testutils "github.com/filecoin-project/bacalhau/pkg/test/utils"
    13  	"github.com/google/uuid"
    14  	"github.com/stretchr/testify/require"
    15  	"github.com/stretchr/testify/suite"
    16  )
    17  
    18  // Define the suite, and absorb the built-in basic suite
    19  // functionality from testify - including a T() method which
    20  // returns the current testing context
    21  type DescribeSuite struct {
    22  	BaseSuite
    23  }
    24  
    25  func (suite *DescribeSuite) TestDescribeJob() {
    26  	tests := []struct {
    27  		numberOfAcceptNodes int
    28  		numberOfRejectNodes int
    29  		jobState            string
    30  	}{
    31  		{numberOfAcceptNodes: 1, numberOfRejectNodes: 0, jobState: model.JobEventResultsPublished.String()}, // Run and accept
    32  		{numberOfAcceptNodes: 2, numberOfRejectNodes: 0, jobState: model.JobEventResultsPublished.String()}, // Run and accept
    33  		{numberOfAcceptNodes: 1, numberOfRejectNodes: 1, jobState: model.JobEventResultsPublished.String()}, // Run and accept
    34  	}
    35  
    36  	numOfJobsTests := []struct {
    37  		numOfJobs int
    38  	}{
    39  		{numOfJobs: 1},
    40  		{numOfJobs: 21}, // one more than the default list length
    41  	}
    42  
    43  	for _, tc := range tests {
    44  		for _, n := range numOfJobsTests {
    45  			func() {
    46  				var submittedJob *model.Job
    47  				ctx := context.Background()
    48  
    49  				for i := 0; i < tc.numberOfAcceptNodes; i++ {
    50  					for k := 0; k < n.numOfJobs; k++ {
    51  						j := testutils.MakeNoopJob()
    52  						j.Spec.Docker.Entrypoint = []string{"Entrypoint-Unique-Array", uuid.NewString()}
    53  						s, err := suite.client.Submit(ctx, j)
    54  						require.NoError(suite.T(), err)
    55  						submittedJob = s // Default to the last job submitted, should be fine?
    56  					}
    57  				}
    58  				returnedJobDescription := &model.JobWithInfo{}
    59  
    60  				// No job id (should error)
    61  				_, out, err := ExecuteTestCobraCommand(suite.T(), "describe",
    62  					"--api-host", suite.host,
    63  					"--api-port", suite.port,
    64  				)
    65  				require.Error(suite.T(), err, "Submitting a describe request with no id should error.")
    66  
    67  				// Job Id at the end
    68  				_, out, err = ExecuteTestCobraCommand(suite.T(), "describe",
    69  					"--api-host", suite.host,
    70  					"--api-port", suite.port,
    71  					submittedJob.Metadata.ID,
    72  				)
    73  				require.NoError(suite.T(), err, "Error in describing job: %+v", err)
    74  
    75  				err = model.YAMLUnmarshalWithMax([]byte(out), returnedJobDescription)
    76  				require.NoError(suite.T(), err, "Error in unmarshalling description: %+v", err)
    77  
    78  				require.Equal(suite.T(), submittedJob.Metadata.ID, returnedJobDescription.Job.Metadata.ID, "IDs do not match.")
    79  				require.Equal(suite.T(),
    80  					submittedJob.Spec.Docker.Entrypoint[0],
    81  					returnedJobDescription.Job.Spec.Docker.Entrypoint[0],
    82  					fmt.Sprintf("Submitted job entrypoints not the same as the description. %d - %d - %s - %d", tc.numberOfAcceptNodes, tc.numberOfRejectNodes, tc.jobState, n.numOfJobs))
    83  
    84  				// Job Id in the middle
    85  				_, out, err = ExecuteTestCobraCommand(suite.T(), "describe",
    86  					"--api-host", suite.host,
    87  					submittedJob.Metadata.ID,
    88  					"--api-port", suite.port,
    89  				)
    90  
    91  				require.NoError(suite.T(), err, "Error in describing job: %+v", err)
    92  				err = model.YAMLUnmarshalWithMax([]byte(out), returnedJobDescription)
    93  				require.NoError(suite.T(), err, "Error in unmarshalling description: %+v", err)
    94  				require.Equal(suite.T(), submittedJob.Metadata.ID, returnedJobDescription.Job.Metadata.ID, "IDs do not match.")
    95  				require.Equal(suite.T(),
    96  					submittedJob.Spec.Docker.Entrypoint[0],
    97  					returnedJobDescription.Job.Spec.Docker.Entrypoint[0],
    98  					fmt.Sprintf("Submitted job entrypoints not the same as the description. %d - %d - %s - %d", tc.numberOfAcceptNodes, tc.numberOfRejectNodes, tc.jobState, n.numOfJobs))
    99  
   100  				// Short job id
   101  				_, out, err = ExecuteTestCobraCommand(suite.T(), "describe",
   102  					"--api-host", suite.host,
   103  					submittedJob.Metadata.ID[0:model.ShortIDLength],
   104  					"--api-port", suite.port,
   105  				)
   106  
   107  				require.NoError(suite.T(), err, "Error in describing job: %+v", err)
   108  				err = model.YAMLUnmarshalWithMax([]byte(out), returnedJobDescription)
   109  				require.NoError(suite.T(), err, "Error in unmarshalling description: %+v", err)
   110  				require.Equal(suite.T(), submittedJob.Metadata.ID, returnedJobDescription.Job.Metadata.ID, "IDs do not match.")
   111  				require.Equal(suite.T(),
   112  					submittedJob.Spec.Docker.Entrypoint[0],
   113  					returnedJobDescription.Job.Spec.Docker.Entrypoint[0],
   114  					fmt.Sprintf("Submitted job entrypoints not the same as the description. %d - %d - %s - %d", tc.numberOfAcceptNodes, tc.numberOfRejectNodes, tc.jobState, n.numOfJobs))
   115  
   116  			}()
   117  		}
   118  	}
   119  
   120  }
   121  
   122  func (suite *DescribeSuite) TestDescribeJobIncludeEvents() {
   123  	tests := []struct {
   124  		includeEvents bool
   125  	}{
   126  		{includeEvents: false},
   127  		{includeEvents: true},
   128  	}
   129  
   130  	for _, tc := range tests {
   131  		func() {
   132  			var submittedJob *model.Job
   133  			ctx := context.Background()
   134  
   135  			j := testutils.MakeNoopJob()
   136  			s, err := suite.client.Submit(ctx, j)
   137  			require.NoError(suite.T(), err)
   138  			submittedJob = s // Default to the last job submitted, should be fine?
   139  
   140  			var returnedJob = &model.Job{}
   141  
   142  			var args []string
   143  
   144  			args = append(args, "describe", "--api-host", suite.host, "--api-port", suite.port, submittedJob.Metadata.ID)
   145  			if tc.includeEvents {
   146  				args = append(args, "--include-events")
   147  			}
   148  
   149  			// Job Id at the end
   150  			_, out, err := ExecuteTestCobraCommand(suite.T(), args...)
   151  			require.NoError(suite.T(), err, "Error in describing job: %+v", err)
   152  
   153  			err = model.YAMLUnmarshalWithMax([]byte(out), &returnedJob)
   154  			require.NoError(suite.T(), err, "Error in unmarshalling description: %+v", err)
   155  
   156  			// TODO: #600 When we figure out how to add events to a noop job, uncomment the below
   157  			// require.True(suite.T(), eventsWereIncluded == tc.includeEvents,
   158  			// 	fmt.Sprintf("Events include: %v\nExpected: %v", eventsWereIncluded, tc.includeEvents))
   159  
   160  			// require.True(suite.T(), localEventsWereIncluded == tc.includeEvents,
   161  			// 	fmt.Sprintf("Events included: %v\nExpected: %v", localEventsWereIncluded, tc.includeEvents))
   162  
   163  		}()
   164  	}
   165  
   166  }
   167  
   168  func (s *DescribeSuite) TestDescribeJobEdgeCases() {
   169  	tests := []struct {
   170  		describeIDEdgecase string
   171  		errorMessage       string
   172  	}{
   173  		{describeIDEdgecase: "", errorMessage: ""},
   174  		{describeIDEdgecase: "BAD_JOB_ID", errorMessage: "No job ID found."},
   175  	}
   176  
   177  	numOfJobsTests := []struct {
   178  		numOfJobs int
   179  	}{
   180  		{numOfJobs: 1}, // just enough that describe could get screwed up
   181  	}
   182  
   183  	for _, tc := range tests {
   184  		for _, n := range numOfJobsTests {
   185  			func() {
   186  				Fatal = FakeFatalErrorHandler
   187  
   188  				var submittedJob *model.Job
   189  				ctx := context.Background()
   190  
   191  				for i := 0; i < n.numOfJobs; i++ {
   192  					j := testutils.MakeNoopJob()
   193  					j.Spec.Docker.Entrypoint = []string{"Entrypoint-Unique-Array", uuid.NewString()}
   194  					jj, err := s.client.Submit(ctx, j)
   195  					require.Nil(s.T(), err)
   196  					submittedJob = jj // Default to the last job submitted, should be fine?
   197  				}
   198  
   199  				var returnedJobDescription = &model.JobWithInfo{}
   200  				var err error
   201  				var out string
   202  				var jobID string
   203  
   204  				// If describeID is empty, should return use submitted ID. Otherwise, use describeID
   205  				if tc.describeIDEdgecase == "" {
   206  					jobID = submittedJob.Metadata.ID
   207  				} else {
   208  					jobID = tc.describeIDEdgecase
   209  				}
   210  
   211  				_, out, err = ExecuteTestCobraCommand(s.T(), "describe",
   212  					"--api-host", s.host,
   213  					"--api-port", s.port,
   214  					jobID,
   215  				)
   216  				if tc.describeIDEdgecase == "" {
   217  					require.NoError(s.T(), err, "Error in describing job: %+v", err)
   218  
   219  					err = model.YAMLUnmarshalWithMax([]byte(out), &returnedJobDescription)
   220  					require.NoError(s.T(), err, "Error in unmarshalling description: %+v", err)
   221  					require.Equal(s.T(), submittedJob.Metadata.ID, returnedJobDescription.Job.Metadata.ID, "IDs do not match.")
   222  					require.Equal(s.T(),
   223  						submittedJob.Spec.Docker.Entrypoint[0],
   224  						returnedJobDescription.Job.Spec.Docker.Entrypoint[0],
   225  						fmt.Sprintf("Submitted job entrypoints not the same as the description. Edgecase: %s", tc.describeIDEdgecase))
   226  				} else {
   227  					c := &model.TestFatalErrorHandlerContents{}
   228  					model.JSONUnmarshalWithMax([]byte(out), &c)
   229  					e := bacerrors.NewJobNotFound(tc.describeIDEdgecase)
   230  					require.Contains(s.T(), c.Message, e.GetMessage(), "Job not found error string not found.", err)
   231  				}
   232  
   233  			}()
   234  		}
   235  	}
   236  
   237  }
   238  
   239  // In order for 'go test' to run this suite, we need to create
   240  // a normal test function and pass our suite to suite.Run
   241  func TestDescribeSuite(t *testing.T) {
   242  	suite.Run(t, new(DescribeSuite))
   243  }