github.com/companieshouse/insolvency-api@v0.0.0-20231024103413-440c973d9e9b/service/insolvency_service_test.go (about)

     1  package service
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"testing"
     9  
    10  	"github.com/companieshouse/insolvency-api/constants"
    11  	"github.com/companieshouse/insolvency-api/mocks"
    12  	"github.com/companieshouse/insolvency-api/models"
    13  	"github.com/golang/mock/gomock"
    14  	"github.com/jarcoal/httpmock"
    15  	. "github.com/smartystreets/goconvey/convey"
    16  	"go.mongodb.org/mongo-driver/bson/primitive"
    17  )
    18  
    19  var transactionID = "12345678"
    20  var companyNumber = "01234567"
    21  var companyName = "companyName"
    22  var req = httptest.NewRequest(http.MethodPut, "/test", nil)
    23  
    24  func createInsolvencyResource() models.InsolvencyResourceDao {
    25  	return models.InsolvencyResourceDao{
    26  		ID:            primitive.ObjectID{},
    27  		TransactionID: transactionID,
    28  		Etag:          "etag1234",
    29  		Kind:          "insolvency",
    30  		Data: models.InsolvencyResourceDaoData{
    31  			CompanyNumber: companyNumber,
    32  			CompanyName:   companyName,
    33  			CaseType:      "insolvency",
    34  			Practitioners: []models.PractitionerResourceDao{
    35  				{
    36  					ID:              "1234",
    37  					IPCode:          "1234",
    38  					FirstName:       "Name",
    39  					LastName:        "LastName",
    40  					TelephoneNumber: "1234",
    41  					Email:           "name@email.com",
    42  					Address:         models.AddressResourceDao{},
    43  					Role:            "final-liquidator",
    44  					Links:           models.PractitionerResourceLinksDao{},
    45  					Appointment: &models.AppointmentResourceDao{
    46  						AppointedOn: "2021-07-07",
    47  						MadeBy:      "creditors",
    48  					},
    49  				},
    50  				{
    51  					ID:              "5678",
    52  					IPCode:          "5678",
    53  					FirstName:       "FirstName",
    54  					LastName:        "LastName",
    55  					TelephoneNumber: "5678",
    56  					Email:           "firstname@email.com",
    57  					Address:         models.AddressResourceDao{},
    58  					Role:            "final-liquidator",
    59  					Links:           models.PractitionerResourceLinksDao{},
    60  					Appointment: &models.AppointmentResourceDao{
    61  						AppointedOn: "2021-07-07",
    62  						MadeBy:      "creditors",
    63  					},
    64  				},
    65  			},
    66  			Attachments: []models.AttachmentResourceDao{
    67  				{
    68  					ID:     "id",
    69  					Type:   "resolution",
    70  					Status: "status",
    71  					Links: models.AttachmentResourceLinksDao{
    72  						Self:     "self",
    73  						Download: "download",
    74  					},
    75  				},
    76  				{
    77  					ID:     "id",
    78  					Type:   "statement-of-affairs-director",
    79  					Status: "status",
    80  					Links: models.AttachmentResourceLinksDao{
    81  						Self:     "self",
    82  						Download: "download",
    83  					},
    84  				},
    85  				{
    86  					ID:     "id",
    87  					Type:   "progress-report",
    88  					Status: "status",
    89  					Links: models.AttachmentResourceLinksDao{
    90  						Self:     "self",
    91  						Download: "download",
    92  					},
    93  				},
    94  			},
    95  			Resolution: &models.ResolutionResourceDao{
    96  				DateOfResolution: "2021-06-06",
    97  				Attachments: []string{
    98  					"id",
    99  				},
   100  			},
   101  			StatementOfAffairs: &models.StatementOfAffairsResourceDao{
   102  				StatementDate: "2021-06-06",
   103  				Attachments: []string{
   104  					"id",
   105  				},
   106  			},
   107  			ProgressReport: &models.ProgressReportResourceDao{
   108  				FromDate: "2021-04-14",
   109  				ToDate:   "2022-04-13",
   110  				Attachments: []string{
   111  					"id",
   112  				},
   113  			},
   114  		},
   115  		Links: models.InsolvencyResourceLinksDao{
   116  			Self:             "/transactions/123456789/insolvency",
   117  			ValidationStatus: "/transactions/123456789/insolvency/validation-status",
   118  		},
   119  	}
   120  }
   121  
   122  func TestUnitValidateInsolvencyDetails(t *testing.T) {
   123  	httpmock.Activate()
   124  	defer httpmock.DeactivateAndReset()
   125  
   126  	Convey("error - one practitioner is appointed but not all practitioners have been appointed", t, func() {
   127  		insolvencyCase := createInsolvencyResource()
   128  
   129  		// Remove appointment for one practitioner
   130  		insolvencyCase.Data.Practitioners[1].Appointment = &models.AppointmentResourceDao{}
   131  
   132  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   133  
   134  		So(validationErrors, ShouldHaveLength, 3)
   135  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - all practitioners for insolvency case with transaction id [%s] must be appointed", insolvencyCase.TransactionID))
   136  		So((*validationErrors)[0].Location, ShouldContainSubstring, "appointment")
   137  	})
   138  
   139  	Convey("error - one practitioner is appointed but not all practitioners have been appointed - missing date", t, func() {
   140  		insolvencyCase := createInsolvencyResource()
   141  
   142  		// Remove appointment for one practitioner
   143  		insolvencyCase.Data.Practitioners[1].Appointment.AppointedOn = ""
   144  
   145  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   146  
   147  		So(validationErrors, ShouldHaveLength, 3)
   148  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - all practitioners for insolvency case with transaction id [%s] must be appointed", insolvencyCase.TransactionID))
   149  		So((*validationErrors)[0].Location, ShouldContainSubstring, "appointment")
   150  	})
   151  
   152  	Convey("successful validation of practitioner appointments - all practitioners appointed", t, func() {
   153  		validationErrors := ValidateInsolvencyDetails(createInsolvencyResource())
   154  		So(validationErrors, ShouldHaveLength, 0)
   155  	})
   156  
   157  	Convey("successful validation of practitioner appointments - no practitioners are appointed", t, func() {
   158  		insolvencyCase := createInsolvencyResource()
   159  
   160  		// Remove appointment details for all practitioners
   161  		insolvencyCase.Data.Practitioners[0].Appointment = nil
   162  		insolvencyCase.Data.Practitioners[1].Appointment = nil
   163  
   164  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   165  		So(validationErrors, ShouldHaveLength, 0)
   166  	})
   167  
   168  	Convey("error - attachment type is not resolution and practitioners key is absent", t, func() {
   169  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
   170  		insolvencyCase := models.InsolvencyResourceDao{
   171  			Data: models.InsolvencyResourceDaoData{
   172  				Attachments: []models.AttachmentResourceDao{
   173  					{
   174  						Type: "type",
   175  					},
   176  				},
   177  			},
   178  		}
   179  
   180  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   181  
   182  		So(validationErrors, ShouldHaveLength, 2)
   183  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - attachment type requires that at least one practitioner must be present for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   184  		So((*validationErrors)[0].Location, ShouldContainSubstring, "resolution attachment type")
   185  	})
   186  
   187  	Convey("error - attachment type is not resolution and practitioners object is empty", t, func() {
   188  		insolvencyCase := models.InsolvencyResourceDao{
   189  			Data: models.InsolvencyResourceDaoData{
   190  				Practitioners: []models.PractitionerResourceDao{},
   191  				Attachments: []models.AttachmentResourceDao{
   192  					{
   193  						Type: "type",
   194  					},
   195  				},
   196  			},
   197  		}
   198  
   199  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   200  
   201  		So(validationErrors, ShouldHaveLength, 2)
   202  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - attachment type requires that at least one practitioner must be present for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   203  		So((*validationErrors)[0].Location, ShouldContainSubstring, "resolution attachment type")
   204  	})
   205  
   206  	Convey("successful validation of attachment type - attachment type is not resolution and practitioner present", t, func() {
   207  		insolvencyCase := createInsolvencyResource()
   208  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   209  		So(validationErrors, ShouldHaveLength, 0)
   210  	})
   211  
   212  	Convey("successful validation of resolution attachment - attachment type is resolution and practitioner present", t, func() {
   213  		insolvencyCase := createInsolvencyResource()
   214  		// Set attachment type to "resolution"
   215  		insolvencyCase.Data.Attachments[0] = models.AttachmentResourceDao{
   216  			Type: "resolution",
   217  			ID:   "1234",
   218  		}
   219  
   220  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   221  			DateOfResolution: "2021-06-06",
   222  			Attachments: []string{
   223  				"1234",
   224  			},
   225  		}
   226  
   227  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   228  		So(validationErrors, ShouldHaveLength, 0)
   229  	})
   230  
   231  	Convey("successful validation of resolution attachment - attachment type is resolution and practitioners key is absent", t, func() {
   232  		insolvencyCase := models.InsolvencyResourceDao{
   233  			Data: models.InsolvencyResourceDaoData{
   234  				Attachments: []models.AttachmentResourceDao{
   235  					{
   236  						Type: "resolution",
   237  						ID:   "1234",
   238  					},
   239  				},
   240  				Resolution: &models.ResolutionResourceDao{
   241  					DateOfResolution: "2021-06-06",
   242  					Attachments: []string{
   243  						"1234",
   244  					},
   245  				},
   246  			},
   247  		}
   248  
   249  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   250  		So(validationErrors, ShouldHaveLength, 0)
   251  	})
   252  
   253  	Convey("successful validation of resolution attachment - attachment type is resolution and practitioners object empty", t, func() {
   254  		insolvencyCase := models.InsolvencyResourceDao{
   255  			Data: models.InsolvencyResourceDaoData{
   256  				Attachments: []models.AttachmentResourceDao{
   257  					{
   258  						Type: "resolution",
   259  						ID:   "1234",
   260  					},
   261  				},
   262  				Resolution: &models.ResolutionResourceDao{
   263  					DateOfResolution: "2021-06-06",
   264  					Attachments: []string{
   265  						"1234",
   266  					},
   267  				},
   268  			},
   269  		}
   270  
   271  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   272  		So(validationErrors, ShouldHaveLength, 0)
   273  	})
   274  
   275  	Convey("successful validation of statement-of-concurrence attachment - attachment type is statement-of-concurrence and statement-of-affairs-director are present", t, func() {
   276  		insolvencyCase := createInsolvencyResource()
   277  		insolvencyCase.Data.Resolution = nil
   278  
   279  		// Set attachment type to "statement-of-concurrence"
   280  		insolvencyCase.Data.Attachments[0].Type = "statement-of-concurrence"
   281  		insolvencyCase.Data.Attachments[1].Type = "statement-of-affairs-director"
   282  
   283  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   284  		So(validationErrors, ShouldHaveLength, 0)
   285  	})
   286  
   287  	Convey("error - attachment type is statement-of-affairs-liquidator and a practitioner is appointed", t, func() {
   288  		insolvencyCase := createInsolvencyResource()
   289  
   290  		// Set attachment type to "statement-of-concurrence"
   291  		insolvencyCase.Data.Attachments[0].Type = "statement-of-affairs-liquidator"
   292  
   293  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   294  
   295  		So(validationErrors, ShouldHaveLength, 2)
   296  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - no appointed practitioners can be assigned to the case when attachment type statement-of-affairs-liquidator is included with transaction id [%s]", insolvencyCase.TransactionID))
   297  		So((*validationErrors)[0].Location, ShouldContainSubstring, "statement of affairs liquidator attachment type")
   298  	})
   299  
   300  	Convey("successful validation of statement-of-affairs-liquidator - attachment type is statement-of-affairs-liquidator and at least one practitioner is present but not appointed", t, func() {
   301  		insolvencyCase := createInsolvencyResource()
   302  
   303  		// Set attachment type to "statement-of-affairs-liquidator"
   304  		insolvencyCase.Data.Attachments[0].Type = "statement-of-affairs-liquidator"
   305  
   306  		// Remove resolution from insolvency case
   307  		insolvencyCase.Data.Resolution = nil
   308  
   309  		// Remove appointment details for all practitioners
   310  		insolvencyCase.Data.Practitioners[0].Appointment = nil
   311  		insolvencyCase.Data.Practitioners[1].Appointment = nil
   312  
   313  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   314  		So(validationErrors, ShouldHaveLength, 0)
   315  	})
   316  
   317  	Convey("error - no attachments present and no appointed practitioners on insolvency case", t, func() {
   318  		insolvencyCase := models.InsolvencyResourceDao{
   319  			Data: models.InsolvencyResourceDaoData{
   320  				Practitioners: []models.PractitionerResourceDao{
   321  					{
   322  						FirstName: "Bob",
   323  					},
   324  				},
   325  			},
   326  		}
   327  
   328  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   329  
   330  		So(validationErrors, ShouldHaveLength, 1)
   331  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - at least one practitioner must be appointed as there are no attachments for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   332  		So((*validationErrors)[0].Location, ShouldContainSubstring, "no attachments")
   333  	})
   334  
   335  	Convey("error - no resolution and no submitted practitioners on insolvency case", t, func() {
   336  		insolvencyCase := models.InsolvencyResourceDao{
   337  			Data: models.InsolvencyResourceDaoData{},
   338  		}
   339  
   340  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   341  
   342  		So(validationErrors, ShouldHaveLength, 1)
   343  		So((*validationErrors)[0].Error, ShouldContainSubstring, "error - if no practitioners are present then an attachment of the type resolution must be present")
   344  		So((*validationErrors)[0].Location, ShouldContainSubstring, "no practitioners and no resolution")
   345  	})
   346  
   347  	Convey("successful validation - no attachments present but at least one appointed practitioner is present on insolvency case", t, func() {
   348  		insolvencyCase := models.InsolvencyResourceDao{
   349  			Data: models.InsolvencyResourceDaoData{
   350  				Practitioners: []models.PractitionerResourceDao{
   351  					{
   352  						Appointment: &models.AppointmentResourceDao{
   353  							AppointedOn: "2020-01-01",
   354  							MadeBy:      "creditors",
   355  						},
   356  					},
   357  				},
   358  			},
   359  		}
   360  
   361  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   362  		So(validationErrors, ShouldHaveLength, 0)
   363  	})
   364  
   365  	Convey("error - resolution attachment present and no date of resolution filed for insolvency case", t, func() {
   366  		insolvencyCase := createInsolvencyResource()
   367  		insolvencyCase.Data.Attachments[0].Type = "resolution"
   368  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   369  			DateOfResolution: "",
   370  			Attachments:      []string{"123"},
   371  		}
   372  
   373  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   374  
   375  		So(validationErrors, ShouldHaveLength, 6)
   376  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - a date of resolution must be present as there is an attachment with type resolution for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   377  		So((*validationErrors)[0].Location, ShouldContainSubstring, "no date of resolution")
   378  	})
   379  
   380  	Convey("error - resolution attachment present and no resolution details filed for insolvency case", t, func() {
   381  		insolvencyCase := createInsolvencyResource()
   382  		insolvencyCase.Data.Attachments[0].Type = "resolution"
   383  		insolvencyCase.Data.Practitioners = nil
   384  		insolvencyCase.Data.Resolution = nil
   385  
   386  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   387  
   388  		So(validationErrors, ShouldHaveLength, 1)
   389  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - a date of resolution must be present as there is an attachment with type resolution for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   390  		So((*validationErrors)[0].Location, ShouldContainSubstring, "no date of resolution")
   391  	})
   392  
   393  	Convey("error - date_of_resolution present and no resolution filed for insolvency case", t, func() {
   394  		insolvencyCase := createInsolvencyResource()
   395  		insolvencyCase.Data.Attachments[0].Type = "test"
   396  
   397  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   398  			DateOfResolution: "2021-06-06",
   399  		}
   400  
   401  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   402  
   403  		So(validationErrors, ShouldHaveLength, 1)
   404  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - a resolution attachment must be present as there is a date_of_resolution filed for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   405  
   406  		So((*validationErrors)[0].Location, ShouldContainSubstring, "no resolution")
   407  	})
   408  
   409  	Convey("error - id for uploaded resolution attachment does not match id supplied with resolution filed for insolvency case", t, func() {
   410  		insolvencyCase := createInsolvencyResource()
   411  		insolvencyCase.Data.Attachments[0] = models.AttachmentResourceDao{
   412  			Type: "resolution",
   413  			ID:   "1234",
   414  		}
   415  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   416  			DateOfResolution: "2021-06-06",
   417  			Attachments: []string{
   418  				"0234",
   419  			},
   420  		}
   421  
   422  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   423  
   424  		So(validationErrors, ShouldHaveLength, 1)
   425  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - id for uploaded resolution attachment must match the attachment id supplied when filing a resolution for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   426  
   427  		So((*validationErrors)[0].Location, ShouldContainSubstring, "attachment ids do not match")
   428  	})
   429  
   430  	Convey("successful validation - resolution attachment present and date of resolution filed for insolvency case", t, func() {
   431  		insolvencyCase := createInsolvencyResource()
   432  		insolvencyCase.Data.Attachments[0] = models.AttachmentResourceDao{
   433  			Type: "resolution",
   434  			ID:   "1234",
   435  		}
   436  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   437  			DateOfResolution: "2021-06-06",
   438  			Attachments: []string{
   439  				"1234",
   440  			},
   441  		}
   442  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   443  		So(validationErrors, ShouldHaveLength, 0)
   444  	})
   445  
   446  	// Loop through SOA attachment types to repeat test to convey the following:
   447  	// error - <attachment type> filed but no statement date/resource exists in DB
   448  	attachmentTypes := []string{constants.StatementOfAffairsDirector.String(), constants.StatementOfAffairsLiquidator.String(), constants.StatementOfConcurrence.String()}
   449  	contextList := []string{"date", "resource"}
   450  	for _, attachment := range attachmentTypes {
   451  		for _, contextItem := range contextList {
   452  			conveyTitle := "error - " + attachment + " filed but no statement " + contextItem + " exists in DB"
   453  			// Convey.. e.g. error - statement-of-affairs-director filed but no statement date exists in DB
   454  			Convey(conveyTitle, t, func() {
   455  				// Create insolvency case
   456  				insolvencyCase := createInsolvencyResource()
   457  				switch contextItem {
   458  				case "date":
   459  					// Remove the date value
   460  					insolvencyCase.Data.StatementOfAffairs = &models.StatementOfAffairsResourceDao{
   461  						StatementDate: "",
   462  					}
   463  				case "resource":
   464  					// Remove the SOA resource
   465  					insolvencyCase.Data.StatementOfAffairs = nil
   466  				}
   467  
   468  				// Change attachment type
   469  				insolvencyCase.Data.Attachments[1].Type = attachment
   470  
   471  				// Remove practitioner for SOA-L to prevent triggering another error
   472  				if attachment == constants.StatementOfAffairsLiquidator.String() {
   473  					insolvencyCase.Data.Practitioners = make([]models.PractitionerResourceDao, 0)
   474  				}
   475  
   476  				validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   477  				So(validationErrors, ShouldHaveLength, 1)
   478  				So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - a date of statement of affairs must be present as there is an attachment with a type of [%s], [%s], or a [%s] for insolvency case with transaction id [%s]", constants.StatementOfAffairsDirector.String(), constants.StatementOfConcurrence.String(), constants.StatementOfAffairsLiquidator.String(), insolvencyCase.TransactionID))
   479  				So((*validationErrors)[0].Location, ShouldContainSubstring, "statement-of-affairs")
   480  			})
   481  		}
   482  	}
   483  
   484  	Convey("error - statement resource exists in DB but no statement-of-affairs attachment filed", t, func() {
   485  
   486  		// Create insolvency case
   487  		insolvencyCase := createInsolvencyResource()
   488  		insolvencyCase.Data.Attachments[1].Type = "random"
   489  
   490  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   491  
   492  		So(validationErrors, ShouldHaveLength, 1)
   493  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - an attachment of type [%s], [%s], or a [%s] must be present as there is a date of statement of affairs present for insolvency case with transaction id [%s]", constants.StatementOfAffairsDirector.String(), constants.StatementOfConcurrence.String(), constants.StatementOfAffairsLiquidator.String(), insolvencyCase.TransactionID))
   494  		So((*validationErrors)[0].Location, ShouldContainSubstring, "statement-of-affairs")
   495  	})
   496  
   497  	Convey("error - attachment type is statement-of-concurrence and practitioner object empty", t, func() {
   498  		insolvencyCase := createInsolvencyResource()
   499  		// Remove the Practitioners
   500  		insolvencyCase.Data.Practitioners = nil
   501  
   502  		// Replace statement-of-affairs-director attachment type to statement-of-concurrence for the 2nd attachment
   503  		insolvencyCase.Data.Attachments[0].Type = "type"
   504  		insolvencyCase.Data.Attachments[1].Type = "statement-of-concurrence"
   505  		insolvencyCase.Data.Attachments[2].Type = "type2"
   506  
   507  		// Remove the resolution and progress report details
   508  		insolvencyCase.Data.Resolution = nil
   509  		insolvencyCase.Data.ProgressReport = nil
   510  
   511  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   512  		So(validationErrors, ShouldHaveLength, 2)
   513  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - attachment type requires that at least one practitioner must be present for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   514  		So((*validationErrors)[1].Error, ShouldContainSubstring, "error - if no practitioners are present then an attachment of the type resolution must be present")
   515  	})
   516  
   517  	Convey("successful validation of statement-of-concurrence - attachment type is statement-of-concurrence and at least one practitioner present", t, func() {
   518  		insolvencyCase := createInsolvencyResource()
   519  
   520  		// Replace statement-of-affairs-director attachment type to statement-of-concurrence for the 2nd attachment
   521  		insolvencyCase.Data.Attachments[0].Type = "type"
   522  		insolvencyCase.Data.Attachments[1].Type = "statement-of-concurrence"
   523  		insolvencyCase.Data.Attachments[2].Type = "type2"
   524  
   525  		// Remove the resolution and progress report details
   526  		insolvencyCase.Data.Resolution = nil
   527  		insolvencyCase.Data.ProgressReport = nil
   528  
   529  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   530  		So(validationErrors, ShouldHaveLength, 0)
   531  	})
   532      
   533  	Convey("error - practitioner appointment is before date of resolution", t, func() {
   534  		// Add resolution to insolvency case
   535  		insolvencyCase := createInsolvencyResource()
   536  		insolvencyCase.Data.Attachments[0] = models.AttachmentResourceDao{
   537  			Type: "resolution",
   538  			ID:   "1234",
   539  		}
   540  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   541  			DateOfResolution: "2021-06-06",
   542  			Attachments: []string{
   543  				"1234",
   544  			},
   545  		}
   546  
   547  		// Appoint practitioner before resolution
   548  		insolvencyCase.Data.Practitioners[0].Appointment.AppointedOn = "2021-05-05"
   549  
   550  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   551  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - practitioner [%s] appointed on [%s] is before the resolution date [%s]", insolvencyCase.Data.Practitioners[0].ID, insolvencyCase.Data.Practitioners[0].Appointment.AppointedOn, insolvencyCase.Data.Resolution.DateOfResolution))
   552  		So((*validationErrors)[0].Location, ShouldContainSubstring, "practitioner")
   553  	})
   554  
   555  	Convey("error parsing appointment date", t, func() {
   556  		// Add resolution to insolvency case
   557  		insolvencyCase := createInsolvencyResource()
   558  		insolvencyCase.Data.Attachments[0] = models.AttachmentResourceDao{
   559  			Type: "resolution",
   560  			ID:   "1234",
   561  		}
   562  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   563  			DateOfResolution: "2021-06-06",
   564  			Attachments: []string{
   565  				"1234",
   566  			},
   567  		}
   568  
   569  		// Appoint practitioner before resolution
   570  		insolvencyCase.Data.Practitioners[0].Appointment.AppointedOn = "date"
   571  
   572  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   573  		So((*validationErrors)[0].Error, ShouldContainSubstring, "cannot parse")
   574  		So((*validationErrors)[0].Location, ShouldContainSubstring, "practitioner")
   575  	})
   576  
   577  	Convey("error parsing resolution date", t, func() {
   578  		// Add resolution to insolvency case
   579  		insolvencyCase := createInsolvencyResource()
   580  		insolvencyCase.Data.Attachments[0] = models.AttachmentResourceDao{
   581  			Type: "resolution",
   582  			ID:   "1234",
   583  		}
   584  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   585  			DateOfResolution: "date",
   586  			Attachments: []string{
   587  				"1234",
   588  			},
   589  		}
   590  		// Appoint practitioner before resolution
   591  		insolvencyCase.Data.Practitioners[0].Appointment.AppointedOn = "2021-05-05"
   592  
   593  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   594  		So((*validationErrors)[0].Error, ShouldContainSubstring, "cannot parse")
   595  		So((*validationErrors)[0].Location, ShouldContainSubstring, "practitioner")
   596  	})
   597  
   598  	Convey("Validate statement date and resolution date", t, func() {
   599  		Convey("Invalid statement date", func() {
   600  			insolvencyCase := createInsolvencyResource()
   601  			insolvencyCase.Data.StatementOfAffairs.StatementDate = "invalid"
   602  
   603  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   604  			So((*validationErrors)[0].Error, ShouldContainSubstring, "invalid statementOfAffairs date")
   605  		})
   606  
   607  		Convey("Invalid resolution date", func() {
   608  			insolvencyCase := createInsolvencyResource()
   609  			insolvencyCase.Data.Resolution.DateOfResolution = "invalid"
   610  			insolvencyCase.Data.Practitioners = nil // prevent alternative validation execution
   611  
   612  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   613  			So((*validationErrors)[0].Error, ShouldContainSubstring, "invalid resolution date")
   614  		})
   615  
   616  		Convey("Statement date is after the resolution date", func() {
   617  			insolvencyCase := createInsolvencyResource()
   618  			insolvencyCase.Data.StatementOfAffairs.StatementDate = "2021-07-26"
   619  			insolvencyCase.Data.Resolution.DateOfResolution = "2021-07-25"
   620  			insolvencyCase.Data.Practitioners = nil // prevent alternative validation execution
   621  
   622  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   623  			So((*validationErrors)[0].Error, ShouldContainSubstring, "error - statement of affairs date [" + insolvencyCase.Data.StatementOfAffairs.StatementDate + "] must not be after the resolution date" + " [" + insolvencyCase.Data.Resolution.DateOfResolution + "]")
   624  		})
   625  
   626  		Convey("Statement date more than 14 days prior to the resolution date", func() {
   627  			insolvencyCase := createInsolvencyResource()
   628  			insolvencyCase.Data.StatementOfAffairs.StatementDate = "2021-07-10"
   629  			insolvencyCase.Data.Resolution.DateOfResolution = "2021-07-25"
   630  			insolvencyCase.Data.Practitioners = nil // prevent alternative validation execution
   631  
   632  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   633  			So((*validationErrors)[0].Error, ShouldContainSubstring, "error - statement of affairs date [" + insolvencyCase.Data.StatementOfAffairs.StatementDate + "] must not be more than 14 days prior to the resolution date" + " [" + insolvencyCase.Data.Resolution.DateOfResolution + "]")
   634  		})
   635  
   636  		Convey("Statement date is 14 days prior to the resolution date", func() {
   637  			insolvencyCase := createInsolvencyResource()
   638  			insolvencyCase.Data.StatementOfAffairs.StatementDate = "2021-07-11"
   639  			insolvencyCase.Data.Resolution.DateOfResolution = "2021-07-25"
   640  			insolvencyCase.Data.Practitioners = nil // prevent alternative validation execution
   641  
   642  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   643  			So(validationErrors, ShouldHaveLength, 0)
   644  		})
   645  
   646  	})
   647  
   648  	Convey("valid insolvency case - appointment date is after resolution date", t, func() {
   649  		// Add resolution to insolvency case
   650  		insolvencyCase := createInsolvencyResource()
   651  		insolvencyCase.Data.Attachments[0] = models.AttachmentResourceDao{
   652  			Type: "resolution",
   653  			ID:   "1234",
   654  		}
   655  		insolvencyCase.Data.Resolution = &models.ResolutionResourceDao{
   656  			DateOfResolution: "2021-06-06",
   657  			Attachments: []string{
   658  				"1234",
   659  			},
   660  		}
   661  
   662  		validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   663  		So(validationErrors, ShouldHaveLength, 0)
   664  	})
   665  
   666  	Convey("Validate progress report from and to dates", t, func() {
   667  		httpmock.Activate()
   668  		defer httpmock.DeactivateAndReset()
   669  
   670  		Convey("valid submission of progress-report", func() {
   671  			insolvencyCase := createInsolvencyResource()
   672  			insolvencyCase.Data.ProgressReport = &models.ProgressReportResourceDao{
   673  				FromDate: "2021-04-14",
   674  				ToDate:   "2022-04-13",
   675  				Attachments: []string{
   676  					"id",
   677  				},
   678  			}
   679  
   680  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   681  			So(validationErrors, ShouldHaveLength, 0)
   682  		})
   683  
   684  		Convey("progress-report attachment present and from date blank", func() {
   685  			insolvencyCase := createInsolvencyResource()
   686  			insolvencyCase.Data.ProgressReport = &models.ProgressReportResourceDao{
   687  				FromDate: "",
   688  				ToDate:   "2022-04-13",
   689  				Attachments: []string{
   690  					"id",
   691  				},
   692  			}
   693  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   694  			So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - progress report dates must be present as there is an attachment with type progress-report for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   695  			So((*validationErrors)[0].Location, ShouldContainSubstring, "no dates for progress report")
   696  		})
   697  
   698  		Convey("progress-report attachment present and to date blank", func() {
   699  			insolvencyCase := createInsolvencyResource()
   700  			insolvencyCase.Data.ProgressReport = &models.ProgressReportResourceDao{
   701  				FromDate: "2021-04-14",
   702  				ToDate:   "",
   703  				Attachments: []string{
   704  					"id",
   705  				},
   706  			}
   707  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   708  			So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - progress report dates must be present as there is an attachment with type progress-report for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   709  			So((*validationErrors)[0].Location, ShouldContainSubstring, "no dates for progress report")
   710  		})
   711  
   712  		Convey("progress-report attachment present and all dates blank", func() {
   713  			insolvencyCase := createInsolvencyResource()
   714  			insolvencyCase.Data.ProgressReport = &models.ProgressReportResourceDao{
   715  				FromDate: "",
   716  				ToDate:   "",
   717  				Attachments: []string{
   718  					"id",
   719  				},
   720  			}
   721  			validationErrors := ValidateInsolvencyDetails(insolvencyCase)
   722  			So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - progress report dates must be present as there is an attachment with type progress-report for insolvency case with transaction id [%s]", insolvencyCase.TransactionID))
   723  			So((*validationErrors)[0].Location, ShouldContainSubstring, "no dates for progress report")
   724  		})
   725  	})
   726  
   727  }
   728  
   729  func TestUnitValidateAntivirus(t *testing.T) {
   730  
   731  	httpmock.Activate()
   732  	defer httpmock.DeactivateAndReset()
   733  
   734  	Convey("error - antivirus check has not been completed", t, func() {
   735  		mockCtrl := gomock.NewController(t)
   736  		defer mockCtrl.Finish()
   737  		mockService := mocks.NewMockService(mockCtrl)
   738  
   739  		insolvencyCase := createInsolvencyResource()
   740  
   741  		attachment := `{
   742  			"name": "file",
   743  			"size": 1000,
   744  			"content_type": "test",
   745  			"av_status": "not-scanned"
   746  			}`
   747  
   748  		// Expect GetAttachmentDetails to be called once and return the attachment
   749  		httpmock.RegisterResponder(http.MethodGet, `=~.*`, httpmock.NewStringResponder(http.StatusOK, attachment))
   750  
   751  		mockService.EXPECT().UpdateAttachmentStatus(transactionID, insolvencyCase.Data.Attachments[0].ID, "integrity_failed").Return(http.StatusNoContent, nil).Times(3)
   752  
   753  		validationErrors := ValidateAntivirus(mockService, insolvencyCase, req)
   754  
   755  		So(validationErrors, ShouldHaveLength, 1)
   756  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - antivirus check has failed on insolvency case with transaction id [%s], attachments have not been scanned", insolvencyCase.TransactionID))
   757  		So((*validationErrors)[0].Location, ShouldContainSubstring, "antivirus incomplete")
   758  	})
   759  
   760  	Convey("error - antivirus check has failed, attachment is infected", t, func() {
   761  		mockCtrl := gomock.NewController(t)
   762  		defer mockCtrl.Finish()
   763  		mockService := mocks.NewMockService(mockCtrl)
   764  
   765  		insolvencyCase := createInsolvencyResource()
   766  
   767  		attachment := `{
   768  			"name": "file",
   769  			"size": 1000,
   770  			"content_type": "test",
   771  			"av_status": "infected"
   772  			}`
   773  
   774  		// Expect GetAttachmentDetails to be called once and return the attachment
   775  		httpmock.RegisterResponder(http.MethodGet, `=~.*`, httpmock.NewStringResponder(http.StatusOK, attachment))
   776  
   777  		mockService.EXPECT().UpdateAttachmentStatus(transactionID, insolvencyCase.Data.Attachments[0].ID, "integrity_failed").Return(http.StatusNoContent, nil).Times(3)
   778  
   779  		validationErrors := ValidateAntivirus(mockService, insolvencyCase, req)
   780  
   781  		So(validationErrors, ShouldHaveLength, 1)
   782  		So((*validationErrors)[0].Error, ShouldContainSubstring, fmt.Sprintf("error - antivirus check has failed on insolvency case with transaction id [%s], virus detected", insolvencyCase.TransactionID))
   783  		So((*validationErrors)[0].Location, ShouldContainSubstring, "antivirus failure")
   784  	})
   785  
   786  	Convey("successful validation - antivirus check has passed, attachment is clean", t, func() {
   787  		mockCtrl := gomock.NewController(t)
   788  		defer mockCtrl.Finish()
   789  		mockService := mocks.NewMockService(mockCtrl)
   790  
   791  		insolvencyCase := createInsolvencyResource()
   792  
   793  		attachment := `{
   794  			"name": "file",
   795  			"size": 1000,
   796  			"content_type": "test",
   797  			"av_status": "clean"
   798  			}`
   799  
   800  		// Expect GetAttachmentDetails to be called once and return the attachment
   801  		httpmock.RegisterResponder(http.MethodGet, `=~.*`, httpmock.NewStringResponder(http.StatusOK, attachment))
   802  
   803  		mockService.EXPECT().UpdateAttachmentStatus(transactionID, insolvencyCase.Data.Attachments[0].ID, "processed").Return(http.StatusNoContent, nil).Times(3)
   804  
   805  		validationErrors := ValidateAntivirus(mockService, insolvencyCase, req)
   806  		So(validationErrors, ShouldHaveLength, 0)
   807  	})
   808  }
   809  
   810  var transactionProfileResponseClosed = `
   811  {
   812   "status": "closed"
   813  }
   814  `
   815  
   816  func TestUnitGenerateFilings(t *testing.T) {
   817  	httpmock.Activate()
   818  	defer httpmock.DeactivateAndReset()
   819  
   820  	Convey("error getting insolvency resource from database", t, func() {
   821  		mockCtrl := gomock.NewController(t)
   822  		defer mockCtrl.Finish()
   823  		mockService := mocks.NewMockService(mockCtrl)
   824  
   825  		// Expect GetInsolvencyResource to be called once and return an error for the insolvency case
   826  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(createInsolvencyResource(), errors.New("insolvency case does not exist")).Times(1)
   827  
   828  		filings, err := GenerateFilings(mockService, transactionID)
   829  
   830  		So(filings, ShouldBeNil)
   831  		So(err.Error(), ShouldContainSubstring, "insolvency case does not exist")
   832  	})
   833  
   834  	Convey("Generate filing for 600 case with two practitioners", t, func() {
   835  		mockCtrl := gomock.NewController(t)
   836  		defer mockCtrl.Finish()
   837  		mockService := mocks.NewMockService(mockCtrl)
   838  
   839  		// Expect the transaction api to be called and return a closed transaction
   840  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
   841  
   842  		insolvencyResource := createInsolvencyResource()
   843  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{}
   844  
   845  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
   846  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
   847  
   848  		filings, err := GenerateFilings(mockService, transactionID)
   849  
   850  		So(len(filings), ShouldEqual, 1)
   851  
   852  		So(filings[0].Kind, ShouldEqual, "insolvency#600")
   853  		So(filings[0].DescriptionIdentifier, ShouldEqual, "600")
   854  		So(filings[0].Data, ShouldContainKey, "practitioners")
   855  		So(filings[0].Data, ShouldNotContainKey, "attachments")
   856  
   857  		So(err, ShouldBeNil)
   858  	})
   859  
   860  	Convey("Generate filing for LRESEX case with resolution attachment and no practitioners", t, func() {
   861  		mockCtrl := gomock.NewController(t)
   862  		defer mockCtrl.Finish()
   863  		mockService := mocks.NewMockService(mockCtrl)
   864  
   865  		// Expect the transaction api to be called and return a closed transaction
   866  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
   867  
   868  		insolvencyResource := createInsolvencyResource()
   869  		insolvencyResource.Data.Practitioners = []models.PractitionerResourceDao{}
   870  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
   871  			{
   872  				ID:     "id",
   873  				Type:   "resolution",
   874  				Status: "status",
   875  				Links: models.AttachmentResourceLinksDao{
   876  					Self:     "self",
   877  					Download: "download",
   878  				},
   879  			},
   880  		}
   881  
   882  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
   883  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
   884  
   885  		filings, err := GenerateFilings(mockService, transactionID)
   886  
   887  		So(len(filings), ShouldEqual, 1)
   888  
   889  		So(filings[0].Kind, ShouldEqual, "insolvency#LRESEX")
   890  		So(filings[0].DescriptionIdentifier, ShouldEqual, "LRESEX")
   891  		So(filings[0].Data, ShouldNotContainKey, "practitioners")
   892  		So(len(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 1)
   893  		So(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "resolution")
   894  
   895  		So(err, ShouldBeNil)
   896  	})
   897  
   898  	Convey("Generate filing for LIQ02 case with statement-of-affairs-director attachment and two practitioners", t, func() {
   899  		mockCtrl := gomock.NewController(t)
   900  		defer mockCtrl.Finish()
   901  		mockService := mocks.NewMockService(mockCtrl)
   902  
   903  		// Expect the transaction api to be called and return a closed transaction
   904  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
   905  
   906  		insolvencyResource := createInsolvencyResource()
   907  		insolvencyResource.Data.Practitioners[0].Appointment = nil
   908  		insolvencyResource.Data.Practitioners[1].Appointment = nil
   909  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
   910  			{
   911  				ID:     "id",
   912  				Type:   "statement-of-affairs-director",
   913  				Status: "status",
   914  				Links: models.AttachmentResourceLinksDao{
   915  					Self:     "self",
   916  					Download: "download",
   917  				},
   918  			},
   919  		}
   920  
   921  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
   922  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
   923  
   924  		filings, err := GenerateFilings(mockService, transactionID)
   925  
   926  		So(len(filings), ShouldEqual, 1)
   927  
   928  		So(filings[0].Kind, ShouldEqual, "insolvency#LIQ02")
   929  		So(filings[0].DescriptionIdentifier, ShouldEqual, "LIQ02")
   930  		So(filings[0].Data, ShouldContainKey, "practitioners")
   931  		So(len(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 1)
   932  		So(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "statement-of-affairs-director")
   933  
   934  		So(err, ShouldBeNil)
   935  	})
   936  
   937  	Convey("Generate filing for LIQ02 case with statement-of-affairs-liquidator attachment and two practitioners", t, func() {
   938  		mockCtrl := gomock.NewController(t)
   939  		defer mockCtrl.Finish()
   940  		mockService := mocks.NewMockService(mockCtrl)
   941  
   942  		// Expect the transaction api to be called and return a closed transaction
   943  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
   944  
   945  		insolvencyResource := createInsolvencyResource()
   946  		insolvencyResource.Data.Practitioners[0].Appointment = nil
   947  		insolvencyResource.Data.Practitioners[1].Appointment = nil
   948  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
   949  			{
   950  				ID:     "id",
   951  				Type:   "statement-of-affairs-liquidator",
   952  				Status: "status",
   953  				Links: models.AttachmentResourceLinksDao{
   954  					Self:     "self",
   955  					Download: "download",
   956  				},
   957  			},
   958  		}
   959  
   960  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
   961  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
   962  
   963  		filings, err := GenerateFilings(mockService, transactionID)
   964  
   965  		So(len(filings), ShouldEqual, 1)
   966  
   967  		So(filings[0].Kind, ShouldEqual, "insolvency#LIQ02")
   968  		So(filings[0].DescriptionIdentifier, ShouldEqual, "LIQ02")
   969  		So(filings[0].Data, ShouldContainKey, "practitioners")
   970  		So(len(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 1)
   971  		So(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "statement-of-affairs-liquidator")
   972  
   973  		So(err, ShouldBeNil)
   974  	})
   975  
   976  	Convey("Generate filing for LIQ02 case with statement-of-affairs-director and statement-of-concurrence attachments and two practitioners", t, func() {
   977  		mockCtrl := gomock.NewController(t)
   978  		defer mockCtrl.Finish()
   979  		mockService := mocks.NewMockService(mockCtrl)
   980  
   981  		// Expect the transaction api to be called and return a closed transaction
   982  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
   983  
   984  		insolvencyResource := createInsolvencyResource()
   985  		insolvencyResource.Data.Practitioners[0].Appointment = nil
   986  		insolvencyResource.Data.Practitioners[1].Appointment = nil
   987  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
   988  			{
   989  				ID:     "id",
   990  				Type:   "statement-of-affairs-director",
   991  				Status: "status",
   992  				Links: models.AttachmentResourceLinksDao{
   993  					Self:     "self",
   994  					Download: "download",
   995  				},
   996  			},
   997  			{
   998  				ID:     "id",
   999  				Type:   "statement-of-concurrence",
  1000  				Status: "status",
  1001  				Links: models.AttachmentResourceLinksDao{
  1002  					Self:     "self",
  1003  					Download: "download",
  1004  				},
  1005  			},
  1006  		}
  1007  
  1008  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
  1009  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
  1010  
  1011  		filings, err := GenerateFilings(mockService, transactionID)
  1012  
  1013  		So(len(filings), ShouldEqual, 1)
  1014  
  1015  		So(filings[0].Kind, ShouldEqual, "insolvency#LIQ02")
  1016  		So(filings[0].DescriptionIdentifier, ShouldEqual, "LIQ02")
  1017  		So(filings[0].Data, ShouldContainKey, "practitioners")
  1018  		So(len(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 2)
  1019  		So(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "statement-of-affairs-director")
  1020  		So(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)[1].Type, ShouldEqual, "statement-of-concurrence")
  1021  
  1022  		So(err, ShouldBeNil)
  1023  	})
  1024  
  1025  	Convey("Generate filing for 600 and LIQ02 case with statement-of-affairs-director attachment and two practitioners", t, func() {
  1026  		mockCtrl := gomock.NewController(t)
  1027  		defer mockCtrl.Finish()
  1028  		mockService := mocks.NewMockService(mockCtrl)
  1029  
  1030  		// Expect the transaction api to be called and return a closed transaction
  1031  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
  1032  
  1033  		insolvencyResource := createInsolvencyResource()
  1034  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
  1035  			{
  1036  				ID:     "id",
  1037  				Type:   "statement-of-affairs-director",
  1038  				Status: "status",
  1039  				Links: models.AttachmentResourceLinksDao{
  1040  					Self:     "self",
  1041  					Download: "download",
  1042  				},
  1043  			},
  1044  		}
  1045  
  1046  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
  1047  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
  1048  
  1049  		filings, err := GenerateFilings(mockService, transactionID)
  1050  
  1051  		So(len(filings), ShouldEqual, 2)
  1052  
  1053  		So(filings[0].Kind, ShouldEqual, "insolvency#600")
  1054  		So(filings[0].DescriptionIdentifier, ShouldEqual, "600")
  1055  		So(filings[0].Data, ShouldContainKey, "practitioners")
  1056  		So(filings[0].Data, ShouldNotContainKey, "attachments")
  1057  
  1058  		So(filings[1].Kind, ShouldEqual, "insolvency#LIQ02")
  1059  		So(filings[1].DescriptionIdentifier, ShouldEqual, "LIQ02")
  1060  		So(filings[1].Data, ShouldContainKey, "practitioners")
  1061  		So(len(filings[1].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 1)
  1062  		So(filings[1].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "statement-of-affairs-director")
  1063  
  1064  		So(err, ShouldBeNil)
  1065  	})
  1066  
  1067  	Convey("Generate filing for 600, LRESEX, and LIQ02 case with statement-of-affairs-director and statement-of-concurrence attachments and two practitioners", t, func() {
  1068  		mockCtrl := gomock.NewController(t)
  1069  		defer mockCtrl.Finish()
  1070  		mockService := mocks.NewMockService(mockCtrl)
  1071  
  1072  		// Expect the transaction api to be called and return a closed transaction
  1073  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
  1074  
  1075  		insolvencyResource := createInsolvencyResource()
  1076  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
  1077  			{
  1078  				ID:     "id",
  1079  				Type:   "resolution",
  1080  				Status: "status",
  1081  				Links: models.AttachmentResourceLinksDao{
  1082  					Self:     "self",
  1083  					Download: "download",
  1084  				},
  1085  			},
  1086  			{
  1087  				ID:     "id",
  1088  				Type:   "statement-of-affairs-director",
  1089  				Status: "status",
  1090  				Links: models.AttachmentResourceLinksDao{
  1091  					Self:     "self",
  1092  					Download: "download",
  1093  				},
  1094  			},
  1095  			{
  1096  				ID:     "id",
  1097  				Type:   "statement-of-concurrence",
  1098  				Status: "status",
  1099  				Links: models.AttachmentResourceLinksDao{
  1100  					Self:     "self",
  1101  					Download: "download",
  1102  				},
  1103  			},
  1104  		}
  1105  
  1106  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
  1107  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
  1108  
  1109  		filings, err := GenerateFilings(mockService, transactionID)
  1110  
  1111  		So(len(filings), ShouldEqual, 3)
  1112  
  1113  		So(filings[0].Kind, ShouldEqual, "insolvency#600")
  1114  		So(filings[0].DescriptionIdentifier, ShouldEqual, "600")
  1115  		So(filings[0].Data, ShouldContainKey, "practitioners")
  1116  		So(filings[0].Data, ShouldNotContainKey, "attachments")
  1117  
  1118  		So(filings[1].Kind, ShouldEqual, "insolvency#LRESEX")
  1119  		So(filings[1].DescriptionIdentifier, ShouldEqual, "LRESEX")
  1120  		So(filings[1].Data, ShouldNotContainKey, "practitioners")
  1121  		So(len(filings[1].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 1)
  1122  		So(filings[1].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "resolution")
  1123  
  1124  		So(filings[2].Kind, ShouldEqual, "insolvency#LIQ02")
  1125  		So(filings[2].DescriptionIdentifier, ShouldEqual, "LIQ02")
  1126  		So(filings[2].Data, ShouldContainKey, "practitioners")
  1127  		So(len(filings[2].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 2)
  1128  		So(filings[2].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "statement-of-affairs-director")
  1129  		So(filings[2].Data["attachments"].([]*models.AttachmentResourceDao)[1].Type, ShouldEqual, "statement-of-concurrence")
  1130  
  1131  		So(err, ShouldBeNil)
  1132  	})
  1133  
  1134  	Convey("Generate filing for LIQ03 case with progress-report attachment and one practitioner", t, func() {
  1135  		mockCtrl := gomock.NewController(t)
  1136  		defer mockCtrl.Finish()
  1137  		mockService := mocks.NewMockService(mockCtrl)
  1138  
  1139  		// Expect the transaction api to be called and return a closed transaction
  1140  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
  1141  
  1142  		insolvencyResource := createInsolvencyResource()
  1143  		insolvencyResource.Data.Practitioners = []models.PractitionerResourceDao{
  1144  			{
  1145  				ID:              "1234",
  1146  				IPCode:          "1234",
  1147  				FirstName:       "Name",
  1148  				LastName:        "LastName",
  1149  				TelephoneNumber: "1234",
  1150  				Email:           "name@email.com",
  1151  				Address:         models.AddressResourceDao{},
  1152  				Role:            "final-liquidator",
  1153  				Links:           models.PractitionerResourceLinksDao{},
  1154  			},
  1155  		}
  1156  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
  1157  			{
  1158  				ID:     "id",
  1159  				Type:   "progress-report",
  1160  				Status: "status",
  1161  				Links: models.AttachmentResourceLinksDao{
  1162  					Self:     "self",
  1163  					Download: "download",
  1164  				},
  1165  			},
  1166  		}
  1167  
  1168  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
  1169  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
  1170  
  1171  		filings, err := GenerateFilings(mockService, transactionID)
  1172  
  1173  		So(len(filings), ShouldEqual, 1)
  1174  
  1175  		So(filings[0].Kind, ShouldEqual, "insolvency#LIQ03")
  1176  		So(filings[0].DescriptionIdentifier, ShouldEqual, "LIQ03")
  1177  		So(filings[0].Data, ShouldContainKey, "practitioners")
  1178  		So(len(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 1)
  1179  		So(filings[0].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "progress-report")
  1180  
  1181  		So(err, ShouldBeNil)
  1182  	})
  1183  
  1184  	Convey("Generate filing for 600 and LIQ03 case with progress-report attachment and one practitioner", t, func() {
  1185  		mockCtrl := gomock.NewController(t)
  1186  		defer mockCtrl.Finish()
  1187  		mockService := mocks.NewMockService(mockCtrl)
  1188  
  1189  		// Expect the transaction api to be called and return a closed transaction
  1190  		httpmock.RegisterResponder(http.MethodGet, "https://api.companieshouse.gov.uk/transactions/12345678", httpmock.NewStringResponder(http.StatusOK, transactionProfileResponseClosed))
  1191  
  1192  		insolvencyResource := createInsolvencyResource()
  1193  		insolvencyResource.Data.Practitioners = []models.PractitionerResourceDao{
  1194  			{
  1195  				ID:              "1234",
  1196  				IPCode:          "1234",
  1197  				FirstName:       "Name",
  1198  				LastName:        "LastName",
  1199  				TelephoneNumber: "1234",
  1200  				Email:           "name@email.com",
  1201  				Address:         models.AddressResourceDao{},
  1202  				Role:            "final-liquidator",
  1203  				Links:           models.PractitionerResourceLinksDao{},
  1204  				Appointment: &models.AppointmentResourceDao{
  1205  					AppointedOn: "2021-07-07",
  1206  					MadeBy:      "creditors",
  1207  				},
  1208  			},
  1209  		}
  1210  		insolvencyResource.Data.Attachments = []models.AttachmentResourceDao{
  1211  			{
  1212  				ID:     "id",
  1213  				Type:   "progress-report",
  1214  				Status: "status",
  1215  				Links: models.AttachmentResourceLinksDao{
  1216  					Self:     "self",
  1217  					Download: "download",
  1218  				},
  1219  			},
  1220  		}
  1221  
  1222  		// Expect GetInsolvencyResource to be called once and return a valid insolvency case
  1223  		mockService.EXPECT().GetInsolvencyResource(transactionID).Return(insolvencyResource, nil).Times(1)
  1224  
  1225  		filings, err := GenerateFilings(mockService, transactionID)
  1226  
  1227  		So(len(filings), ShouldEqual, 2)
  1228  
  1229  		So(filings[0].Kind, ShouldEqual, "insolvency#600")
  1230  		So(filings[0].DescriptionIdentifier, ShouldEqual, "600")
  1231  		So(filings[0].Data, ShouldContainKey, "practitioners")
  1232  		So(filings[0].Data, ShouldNotContainKey, "attachments")
  1233  
  1234  		So(filings[1].Kind, ShouldEqual, "insolvency#LIQ03")
  1235  		So(filings[1].DescriptionIdentifier, ShouldEqual, "LIQ03")
  1236  		So(filings[1].Data, ShouldContainKey, "practitioners")
  1237  		So(len(filings[1].Data["attachments"].([]*models.AttachmentResourceDao)), ShouldEqual, 1)
  1238  		So(filings[1].Data["attachments"].([]*models.AttachmentResourceDao)[0].Type, ShouldEqual, "progress-report")
  1239  
  1240  		So(err, ShouldBeNil)
  1241  	})
  1242  
  1243  }