From d153735dee1c19f45ac94e3d14f509189f191145 Mon Sep 17 00:00:00 2001 From: buke Date: Sat, 25 Jul 2026 23:21:20 +0800 Subject: [PATCH 1/3] refactor(webapistore): resolve base service names from BaseModel meta - Resolve IsBaseService names from abstract BaseModel by path instead of a hardcoded map. - Fail generation when BaseModel meta is missing or has no conventional services. - Document that the FE base surface remains the hand-written WebModelStore service section. Co-authored-by: Cursor --- .../artifact/generate/generator_test.go | 48 +++++++++ .../module/artifact/generate/webapistore.go | 82 ++++++++++------ .../artifact/generate/webapistore_test.go | 98 +++++++++++++++++++ modules/web/web/stores/modelStore.ts | 5 +- 4 files changed, 202 insertions(+), 31 deletions(-) diff --git a/internal/module/artifact/generate/generator_test.go b/internal/module/artifact/generate/generator_test.go index 81e2189f3..7735c2986 100644 --- a/internal/module/artifact/generate/generator_test.go +++ b/internal/module/artifact/generate/generator_test.go @@ -11,9 +11,12 @@ import ( "os" "path/filepath" "reflect" + "strconv" + "strings" "testing" "time" + "github.com/choysum-dev/choysum/internal/esbplugins" "github.com/choysum-dev/choysum/internal/esbplugins/backendplugin" module "github.com/choysum-dev/choysum/internal/module/artifact/result" "github.com/choysum-dev/choysum/internal/parser/backendtsparser" @@ -85,6 +88,50 @@ func seedGeneratorMetaTables(t *testing.T, runtimeScope *generatorTestScope) { } } +// seedAbstractBaseModel inserts an abstract BaseModel IrModel at BaseModelModuleSpec path +// with conventional services. Pass nil for the default name set; pass a non-nil empty +// slice to seed BaseModel with no services. +func seedAbstractBaseModel(t *testing.T, runtimeScope *generatorTestScope, names []string) { + t.Helper() + seedGeneratorMetaTables(t, runtimeScope) + + path, _ := meta.BaseModelModuleSpec(runtimeScope) + path = esbplugins.NormalizePath(path) + if !strings.HasSuffix(path, ".ts") { + path = path + ".ts" + } + if names == nil { + names = []string{ + "Browse", "BrowseMany", "Search", "NameSearch", "Copy", "Count", + "Create", "CreateMany", "Update", "UpdateById", "Delete", "DeleteById", + "DefaultGet", "FieldsGet", "GetFieldTranslations", "UpdateFieldTranslations", + "Onchange", "ReadGroup", "ReadGroupCount", + } + } + + model := &meta.IrModel{ + BaseModel: meta.BaseModel{Id: sql.NullString{String: "abstract-base-model", Valid: true}}, + Name: "BaseModel", + Path: path, + Abstract: true, + } + if err := runtimeScope.db.Create(model).Error; err != nil { + t.Fatalf("create abstract BaseModel: %v", err) + } + for i, name := range names { + svc := &meta.IrService{ + BaseModel: meta.BaseModel{Id: sql.NullString{String: "base-svc-" + strconv.Itoa(i), Valid: true}}, + Name: name, + AccessibilityModifier: "public", + IsStatic: true, + ModelId: model.Id, + } + if err := runtimeScope.db.Create(svc).Error; err != nil { + t.Fatalf("create BaseModel service %s: %v", name, err) + } + } +} + func seedGeneratorAppFixture(t *testing.T, runtimeScope *generatorTestScope) (*meta.IrApplication, *meta.IrModule) { t.Helper() seedGeneratorMetaTables(t, runtimeScope) @@ -467,6 +514,7 @@ func TestGeneratorEntryPoints(t *testing.T) { t.Run("GenerateCtx runs full success pipeline", func(t *testing.T) { runtimeScope := newGeneratorScope(t) _, mod := seedGeneratorAppFixture(t, runtimeScope) + seedAbstractBaseModel(t, runtimeScope, nil) protoDir := t.TempDir() webDir := t.TempDir() serviceDir := t.TempDir() diff --git a/internal/module/artifact/generate/webapistore.go b/internal/module/artifact/generate/webapistore.go index a4eaae35b..904a1f353 100644 --- a/internal/module/artifact/generate/webapistore.go +++ b/internal/module/artifact/generate/webapistore.go @@ -8,18 +8,21 @@ import ( "context" _ "embed" "encoding/json" + "errors" "os" "path/filepath" "strconv" "strings" "text/template" + "github.com/choysum-dev/choysum/internal/esbplugins" module "github.com/choysum-dev/choysum/internal/module/artifact/result" "github.com/choysum-dev/choysum/internal/module/artifact/staging" "github.com/choysum-dev/choysum/pkg/meta" "github.com/choysum-dev/choysum/pkg/scope" "github.com/ettle/strcase" xfmt "golang.org/x/exp/errors/fmt" + "gorm.io/gorm" ) //go:embed webapistore.ts.tpl @@ -324,6 +327,51 @@ func analyzeRelationFields(model *meta.IrModel) ([]RelationFieldInfo, []string) return relationFields, importModels } +// resolveBaseServiceNames loads conventional service names from the abstract BaseModel +// IrModel (by BaseModelModuleSpec path). These names are filtered out of generated +// XxxStore interfaces because they are already declared on the hand-written WebModelStore. +// Do not maintain a parallel hardcoded name list here. +func resolveBaseServiceNames(runtimeScope scope.Scope) (map[string]bool, error) { + path, _ := meta.BaseModelModuleSpec(runtimeScope) + path = strings.TrimSpace(path) + if path == "" { + return nil, xfmt.Errorf("base model module path is empty") + } + path = esbplugins.NormalizePath(path) + pathNoExt := strings.TrimSuffix(path, ".ts") + pathWithExt := pathNoExt + ".ts" + + var model meta.IrModel + result := runtimeScope.Session(). + Preload("Services", func(db *gorm.DB) *gorm.DB { + return db.Order("id ASC") + }). + Where("path = ? OR path = ?", pathNoExt, pathWithExt). + Order("id DESC"). + Take(&model) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, xfmt.Errorf("abstract BaseModel not found at path %s (or %s)", pathNoExt, pathWithExt) + } + return nil, xfmt.Errorf("load abstract BaseModel by path %s: %w", pathWithExt, result.Error) + } + + names := make(map[string]bool) + for _, svc := range model.Services { + if svc == nil { + continue + } + if !meta.IsConventionalModelService(svc.AccessibilityModifier, svc.IsStatic, svc.Name) { + continue + } + names[svc.Name] = true + } + if len(names) == 0 { + return nil, xfmt.Errorf("abstract BaseModel at path %s has no conventional services", model.Path) + } + return names, nil +} + func (g *webApiStoreGenerator) generate(ctx context.Context, app *meta.IrApplication) ([]*module.GeneratorResult, error) { if ctx == nil { ctx = context.Background() @@ -334,35 +382,9 @@ func (g *webApiStoreGenerator) generate(ctx context.Context, app *meta.IrApplica return nil, nil } - // Preload BaseModel service names so the template can filter base CRUD services. - baseServiceNames := map[string]bool{ - "Browse": true, - "BrowseMany": true, - "Search": true, - "NameSearch": true, - "Copy": true, - "Count": true, - "Create": true, - "CreateMany": true, - "Update": true, - "UpdateById": true, - "Delete": true, - "DeleteById": true, - "DefaultGet": true, - "FieldsGet": true, - "GetFieldTranslations": true, - "UpdateFieldTranslations": true, - "Onchange": true, - "ReadGroup": true, - "ReadGroupCount": true, - } - for _, m := range app.Models { - if m.Name == "BaseModel" { - for _, svc := range m.Services { - baseServiceNames[svc.Name] = true - } - break - } + baseServiceNames, err := resolveBaseServiceNames(g.runtimeScope) + if err != nil { + return nil, err } funcMap := template.FuncMap{ @@ -373,7 +395,7 @@ func (g *webApiStoreGenerator) generate(ctx context.Context, app *meta.IrApplica p := strings.ReplaceAll(path, runtimeOpts.modulesPath, "@") return strings.TrimSuffix(p, ".ts") }, - // Check whether the service name comes from BaseModel. + // True when name is a BaseModel conventional service (already on WebModelStore). "IsBaseService": func(name string) bool { return baseServiceNames[name] }, diff --git a/internal/module/artifact/generate/webapistore_test.go b/internal/module/artifact/generate/webapistore_test.go index b517507f7..74fa0e644 100644 --- a/internal/module/artifact/generate/webapistore_test.go +++ b/internal/module/artifact/generate/webapistore_test.go @@ -13,12 +13,14 @@ import ( "testing" "text/template" + "github.com/choysum-dev/choysum/internal/esbplugins" "github.com/choysum-dev/choysum/internal/module/artifact/staging" "github.com/choysum-dev/choysum/pkg/meta" ) func TestWebApiStoreGenerate(t *testing.T) { runtimeScope := newGeneratorScope(t) + seedAbstractBaseModel(t, runtimeScope, nil) referenceKey := meta.TermReferenceKey("demo", "demo.status.allow", "Allow", "literal") stringKey := meta.TermReferenceKey("demo", "demo.model.Partner.fields", "Amount", "literal") selectionJSON := `[{"value":"allow","label":"Allow","labelText":{"key":"` + referenceKey + `","module":"demo","scope":"demo.status.allow","src":"Allow","kind":"literal"}}]` @@ -270,6 +272,7 @@ func TestWebApiStoreGenerateEmptyApp(t *testing.T) { func TestWebApiStoreGenerate_UsesWorkspaceGeneratedTargets(t *testing.T) { runtimeScope := newGeneratorScope(t) + seedAbstractBaseModel(t, runtimeScope, nil) _, webDir, _, err := WorkspaceGeneratedAPITargets(runtimeScope.cfg.ModulesPath, "crm", runtimeScope.cfg.DefaultChoysumPath) if err != nil { t.Fatalf("WorkspaceGeneratedAPITargets() error = %v", err) @@ -294,6 +297,7 @@ func TestWebApiStoreGenerate_UsesWorkspaceGeneratedTargets(t *testing.T) { func TestWebApiStoreGenerate_WorkspaceTargetsRequireDefaultChoysumPath(t *testing.T) { runtimeScope := newGeneratorScope(t) + seedAbstractBaseModel(t, runtimeScope, nil) runtimeScope.cfg.DefaultChoysumPath = "" _, err := (&webApiStoreGenerator{runtimeScope: runtimeScope, module: &meta.IrModule{ApplicationStr: "crm"}}).generate(context.Background(), testApp()) @@ -302,6 +306,100 @@ func TestWebApiStoreGenerate_WorkspaceTargetsRequireDefaultChoysumPath(t *testin } } +func TestWebApiStoreGenerate_FiltersBaseServicesFromInterface(t *testing.T) { + runtimeScope := newGeneratorScope(t) + seedAbstractBaseModel(t, runtimeScope, nil) + + app := &meta.IrApplication{ + Name: "crm", + Models: []*meta.IrModel{{ + Name: "Partner", + Path: "@/crm/service/models/partner.ts", + Services: []*meta.IrService{ + {Name: "Search", AccessibilityModifier: "public", IsStatic: true}, + {Name: "NameSearch", AccessibilityModifier: "public", IsStatic: true}, + {Name: "Copy", AccessibilityModifier: "public", IsStatic: true}, + {Name: "CreatePartner", AccessibilityModifier: "public", IsStatic: true, Parameters: []*meta.IrParameter{{Name: "partner_id", ProtobufType: "string"}}}, + }, + }}, + } + webStoreDir := t.TempDir() + _, err := (&webApiStoreGenerator{runtimeScope: runtimeScope, module: &meta.IrModule{ApplicationStr: "crm"}, modulesWebDir: webStoreDir}).generate(context.Background(), app) + if err != nil { + t.Fatalf("generate() error = %v", err) + } + storeContent, err := os.ReadFile(filepath.Join(webStoreDir, "stores", "partner.ts")) + if err != nil { + t.Fatalf("read partner.ts: %v", err) + } + content := string(storeContent) + if !strings.Contains(content, "CreatePartner:") { + t.Fatalf("expected custom CreatePartner on PartnerStore interface, got:\n%s", content) + } + for _, baseName := range []string{"Search", "NameSearch", "Copy"} { + // Interface lines look like: " Search: (...args:" + needle := " " + baseName + ": (...args:" + if strings.Contains(content, needle) { + t.Fatalf("base service %s must not be redeclared on PartnerStore interface, got:\n%s", baseName, content) + } + } +} + +func TestWebApiStoreGenerate_MissingBaseModelErrors(t *testing.T) { + runtimeScope := newGeneratorScope(t) + seedGeneratorMetaTables(t, runtimeScope) + + _, err := (&webApiStoreGenerator{runtimeScope: runtimeScope, module: &meta.IrModule{ApplicationStr: "crm"}, modulesWebDir: t.TempDir()}).generate(context.Background(), testApp()) + if err == nil || !strings.Contains(err.Error(), "BaseModel not found") { + t.Fatalf("expected BaseModel not found error, got %v", err) + } +} + +func TestWebApiStoreGenerate_EmptyBaseModelServicesErrors(t *testing.T) { + runtimeScope := newGeneratorScope(t) + seedAbstractBaseModel(t, runtimeScope, []string{}) + + _, err := (&webApiStoreGenerator{runtimeScope: runtimeScope, module: &meta.IrModule{ApplicationStr: "crm"}, modulesWebDir: t.TempDir()}).generate(context.Background(), testApp()) + if err == nil || !strings.Contains(err.Error(), "no conventional services") { + t.Fatalf("expected empty conventional services error, got %v", err) + } +} + +func TestResolveBaseServiceNames_RejectsNonConventionalOnly(t *testing.T) { + runtimeScope := newGeneratorScope(t) + seedGeneratorMetaTables(t, runtimeScope) + + path, _ := meta.BaseModelModuleSpec(runtimeScope) + path = esbplugins.NormalizePath(path) + if !strings.HasSuffix(path, ".ts") { + path = path + ".ts" + } + model := &meta.IrModel{ + BaseModel: meta.BaseModel{Id: sql.NullString{String: "abstract-base-model", Valid: true}}, + Name: "BaseModel", + Path: path, + Abstract: true, + } + if err := runtimeScope.db.Create(model).Error; err != nil { + t.Fatalf("create BaseModel: %v", err) + } + svc := &meta.IrService{ + BaseModel: meta.BaseModel{Id: sql.NullString{String: "svc-helper", Valid: true}}, + Name: "helper", + AccessibilityModifier: "public", + IsStatic: true, + ModelId: model.Id, + } + if err := runtimeScope.db.Create(svc).Error; err != nil { + t.Fatalf("create helper service: %v", err) + } + + _, err := resolveBaseServiceNames(runtimeScope) + if err == nil || !strings.Contains(err.Error(), "no conventional services") { + t.Fatalf("expected no conventional services error, got %v", err) + } +} + func TestConvertFieldToMetadata_TranslateContract(t *testing.T) { trueVal := true size := 200 diff --git a/modules/web/web/stores/modelStore.ts b/modules/web/web/stores/modelStore.ts index e2e3b6c66..6a4d87af9 100644 --- a/modules/web/web/stores/modelStore.ts +++ b/modules/web/web/stores/modelStore.ts @@ -123,7 +123,10 @@ export interface WebModelStore extends ScopedStore { getContext: () => Record; withContext: (ctx: Record, fn: () => Promise) => Promise; - // Base services, optionally populated by generated templates. + // Base ORM RPCs shared by generated XxxStore (Go IsBaseService filters these names). + // When adding a BaseModel public static async PascalCase method for the FE base surface, + // declare it here; webapistore resolves names from BaseModel meta — do not maintain a + // parallel name list in webapistore.go. DefaultGet: ClientModelService>; Create: ClientModelService>; CreateMany: ClientModelService>; From 0a98bbf1da14acb4016b51c4f600a9c286531013 Mon Sep 17 00:00:00 2001 From: buke Date: Sat, 25 Jul 2026 23:37:16 +0800 Subject: [PATCH 2/3] fix(webapistore): require abstract BaseModel when resolving service names - Constrain the BaseModel path lookup with abstract=true so a concrete model at the same path cannot supply IsBaseService names. - Add a regression test covering a non-abstract model at the BaseModel path. Co-authored-by: Cursor --- .../module/artifact/generate/webapistore.go | 2 +- .../artifact/generate/webapistore_test.go | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/internal/module/artifact/generate/webapistore.go b/internal/module/artifact/generate/webapistore.go index 904a1f353..fadb75198 100644 --- a/internal/module/artifact/generate/webapistore.go +++ b/internal/module/artifact/generate/webapistore.go @@ -346,7 +346,7 @@ func resolveBaseServiceNames(runtimeScope scope.Scope) (map[string]bool, error) Preload("Services", func(db *gorm.DB) *gorm.DB { return db.Order("id ASC") }). - Where("path = ? OR path = ?", pathNoExt, pathWithExt). + Where("(path = ? OR path = ?) AND abstract = ?", pathNoExt, pathWithExt, true). Order("id DESC"). Take(&model) if result.Error != nil { diff --git a/internal/module/artifact/generate/webapistore_test.go b/internal/module/artifact/generate/webapistore_test.go index 74fa0e644..acd4c64ed 100644 --- a/internal/module/artifact/generate/webapistore_test.go +++ b/internal/module/artifact/generate/webapistore_test.go @@ -400,6 +400,41 @@ func TestResolveBaseServiceNames_RejectsNonConventionalOnly(t *testing.T) { } } +func TestResolveBaseServiceNames_RequiresAbstract(t *testing.T) { + runtimeScope := newGeneratorScope(t) + seedGeneratorMetaTables(t, runtimeScope) + + path, _ := meta.BaseModelModuleSpec(runtimeScope) + path = esbplugins.NormalizePath(path) + if !strings.HasSuffix(path, ".ts") { + path = path + ".ts" + } + model := &meta.IrModel{ + BaseModel: meta.BaseModel{Id: sql.NullString{String: "concrete-at-base-path", Valid: true}}, + Name: "BaseModel", + Path: path, + Abstract: false, + } + if err := runtimeScope.db.Create(model).Error; err != nil { + t.Fatalf("create non-abstract model: %v", err) + } + svc := &meta.IrService{ + BaseModel: meta.BaseModel{Id: sql.NullString{String: "svc-search", Valid: true}}, + Name: "Search", + AccessibilityModifier: "public", + IsStatic: true, + ModelId: model.Id, + } + if err := runtimeScope.db.Create(svc).Error; err != nil { + t.Fatalf("create Search service: %v", err) + } + + _, err := resolveBaseServiceNames(runtimeScope) + if err == nil || !strings.Contains(err.Error(), "BaseModel not found") { + t.Fatalf("expected abstract BaseModel not found error, got %v", err) + } +} + func TestConvertFieldToMetadata_TranslateContract(t *testing.T) { trueVal := true size := 200 From c1b33baf61f3fc25ef98cb7d6247a50fb09b83b4 Mon Sep 17 00:00:00 2001 From: buke Date: Sat, 25 Jul 2026 23:39:08 +0800 Subject: [PATCH 3/3] test(webapistore): cover BaseModel service-name resolution edge cases - Extract path/services helpers so empty path, nil services, and non-NotFound load errors are unit-testable. - Add nil-context generate coverage for the resolve wiring path. Co-authored-by: Cursor --- .../module/artifact/generate/webapistore.go | 19 ++++-- .../artifact/generate/webapistore_test.go | 64 +++++++++++++++++++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/internal/module/artifact/generate/webapistore.go b/internal/module/artifact/generate/webapistore.go index fadb75198..a71a8e0a9 100644 --- a/internal/module/artifact/generate/webapistore.go +++ b/internal/module/artifact/generate/webapistore.go @@ -333,6 +333,10 @@ func analyzeRelationFields(model *meta.IrModel) ([]RelationFieldInfo, []string) // Do not maintain a parallel hardcoded name list here. func resolveBaseServiceNames(runtimeScope scope.Scope) (map[string]bool, error) { path, _ := meta.BaseModelModuleSpec(runtimeScope) + return resolveBaseServiceNamesAtPath(runtimeScope, path) +} + +func resolveBaseServiceNamesAtPath(runtimeScope scope.Scope, path string) (map[string]bool, error) { path = strings.TrimSpace(path) if path == "" { return nil, xfmt.Errorf("base model module path is empty") @@ -356,8 +360,16 @@ func resolveBaseServiceNames(runtimeScope scope.Scope) (map[string]bool, error) return nil, xfmt.Errorf("load abstract BaseModel by path %s: %w", pathWithExt, result.Error) } + names := conventionalBaseServiceNames(model.Services) + if len(names) == 0 { + return nil, xfmt.Errorf("abstract BaseModel at path %s has no conventional services", model.Path) + } + return names, nil +} + +func conventionalBaseServiceNames(services []*meta.IrService) map[string]bool { names := make(map[string]bool) - for _, svc := range model.Services { + for _, svc := range services { if svc == nil { continue } @@ -366,10 +378,7 @@ func resolveBaseServiceNames(runtimeScope scope.Scope) (map[string]bool, error) } names[svc.Name] = true } - if len(names) == 0 { - return nil, xfmt.Errorf("abstract BaseModel at path %s has no conventional services", model.Path) - } - return names, nil + return names } func (g *webApiStoreGenerator) generate(ctx context.Context, app *meta.IrApplication) ([]*module.GeneratorResult, error) { diff --git a/internal/module/artifact/generate/webapistore_test.go b/internal/module/artifact/generate/webapistore_test.go index acd4c64ed..6ee8a43ed 100644 --- a/internal/module/artifact/generate/webapistore_test.go +++ b/internal/module/artifact/generate/webapistore_test.go @@ -270,6 +270,30 @@ func TestWebApiStoreGenerateEmptyApp(t *testing.T) { } } +func TestWebApiStoreGenerate_NilContext(t *testing.T) { + runtimeScope := newGeneratorScope(t) + seedAbstractBaseModel(t, runtimeScope, nil) + webStoreDir := t.TempDir() + results, err := (&webApiStoreGenerator{runtimeScope: runtimeScope, module: &meta.IrModule{ApplicationStr: "crm"}, modulesWebDir: webStoreDir}).generate(nil, &meta.IrApplication{ + Name: "crm", + Models: []*meta.IrModel{{ + Name: "Partner", + Path: "@/crm/service/models/partner.ts", + Services: []*meta.IrService{{ + Name: "CreatePartner", + AccessibilityModifier: "public", + IsStatic: true, + }}, + }}, + }) + if err != nil { + t.Fatalf("generate(nil ctx) error = %v", err) + } + if len(results) != 1 { + t.Fatalf("unexpected results: %#v", results) + } +} + func TestWebApiStoreGenerate_UsesWorkspaceGeneratedTargets(t *testing.T) { runtimeScope := newGeneratorScope(t) seedAbstractBaseModel(t, runtimeScope, nil) @@ -435,6 +459,46 @@ func TestResolveBaseServiceNames_RequiresAbstract(t *testing.T) { } } +func TestResolveBaseServiceNamesAtPath_EmptyPath(t *testing.T) { + runtimeScope := newGeneratorScope(t) + _, err := resolveBaseServiceNamesAtPath(runtimeScope, " ") + if err == nil || !strings.Contains(err.Error(), "base model module path is empty") { + t.Fatalf("expected empty path error, got %v", err) + } +} + +func TestResolveBaseServiceNames_LoadError(t *testing.T) { + runtimeScope := newGeneratorScope(t) + seedGeneratorMetaTables(t, runtimeScope) + sqlDB, err := runtimeScope.db.DB() + if err != nil { + t.Fatalf("db: %v", err) + } + if err := sqlDB.Close(); err != nil { + t.Fatalf("close db: %v", err) + } + + _, err = resolveBaseServiceNames(runtimeScope) + if err == nil || !strings.Contains(err.Error(), "load abstract BaseModel by path") { + t.Fatalf("expected load error, got %v", err) + } +} + +func TestConventionalBaseServiceNames_SkipsNilAndNonConventional(t *testing.T) { + names := conventionalBaseServiceNames([]*meta.IrService{ + nil, + {Name: "helper", AccessibilityModifier: "public", IsStatic: true}, + {Name: "Search", AccessibilityModifier: "public", IsStatic: true}, + {Name: "Browse", AccessibilityModifier: "private", IsStatic: true}, + }) + if !names["Search"] { + t.Fatalf("expected Search, got %#v", names) + } + if names["helper"] || names["Browse"] || len(names) != 1 { + t.Fatalf("unexpected names: %#v", names) + } +} + func TestConvertFieldToMetadata_TranslateContract(t *testing.T) { trueVal := true size := 200