Skip to content

Commit 02e7ab3

Browse files
committed
Convert test/qa-tests/jstests/import/fields.js to Go
1 parent caace78 commit 02e7ab3

2 files changed

Lines changed: 244 additions & 107 deletions

File tree

mongoimport/mongoimport_test.go

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"bufio"
1111
"bytes"
1212
"context"
13+
"encoding/csv"
1314
"encoding/json"
1415
"fmt"
1516
"io"
@@ -2772,3 +2773,246 @@ func TestRoundTripDecimal128(t *testing.T) {
27722773
require.NoError(t, err)
27732774
assert.Equal(t, testDoc, result, "imported doc should match original")
27742775
}
2776+
2777+
// TestImportFields verifies --headerline, --fields, --fieldFile, --ignoreBlanks,
2778+
// nested dotted field names, and extra-fields-beyond-header behavior.
2779+
func TestImportFields(t *testing.T) {
2780+
testtype.SkipUnlessTestType(t, testtype.IntegrationTestType)
2781+
2782+
const dbName = "mongoimport_fields_test"
2783+
2784+
sessionProvider, _, err := testutil.GetBareSessionProvider()
2785+
require.NoError(t, err)
2786+
client, err := sessionProvider.GetSession()
2787+
require.NoError(t, err)
2788+
t.Cleanup(func() {
2789+
if err := client.Database(dbName).Drop(context.Background()); err != nil {
2790+
t.Errorf("dropping test database: %v", err)
2791+
}
2792+
})
2793+
2794+
tmpDir := t.TempDir()
2795+
header := []string{"a", "b", "c.xyz", "d.hij.lkm"}
2796+
rows := [][]string{
2797+
{"foo", "bar", "blah", "qwz"},
2798+
{"bob", "", "steve", "sue"},
2799+
{"one", "two", "three", "four"},
2800+
}
2801+
2802+
for _, format := range []string{"csv", "tsv"} {
2803+
testImportFieldsForFormat(t, client, dbName, tmpDir, format, header, rows)
2804+
}
2805+
}
2806+
2807+
func testImportFieldsForFormat(
2808+
t *testing.T,
2809+
client *mongo.Client,
2810+
dbName, tmpDir, format string,
2811+
header []string,
2812+
rows [][]string,
2813+
) {
2814+
t.Helper()
2815+
2816+
separator, ok := map[string]rune{
2817+
"csv": ',',
2818+
"tsv": '\t',
2819+
}[format]
2820+
require.True(t, ok, "found a separator for %#q format", format)
2821+
2822+
headerFile := filepath.Join(tmpDir, "header."+format)
2823+
writeXSVFile(t, headerFile, separator, append([][]string{header}, rows...))
2824+
2825+
noHeaderFile := filepath.Join(tmpDir, "noheader."+format)
2826+
writeXSVFile(t, noHeaderFile, separator, rows)
2827+
fieldFilePath := writeFieldFile(t, tmpDir, "fieldfile."+format, header)
2828+
2829+
t.Run(format+"/headerline", func(t *testing.T) {
2830+
importAndCheckFields(t, client, importFieldsOpts{
2831+
dbName: dbName,
2832+
inputFile: headerFile,
2833+
format: format,
2834+
headerLine: true,
2835+
})
2836+
})
2837+
2838+
t.Run(format+"/fields", func(t *testing.T) {
2839+
fields := "a,b,c.xyz,d.hij.lkm"
2840+
importAndCheckFields(t, client, importFieldsOpts{
2841+
dbName: dbName,
2842+
inputFile: noHeaderFile,
2843+
format: format,
2844+
fields: &fields,
2845+
})
2846+
})
2847+
2848+
t.Run(format+"/fieldFile", func(t *testing.T) {
2849+
importAndCheckFields(t, client, importFieldsOpts{
2850+
dbName: dbName,
2851+
inputFile: noHeaderFile,
2852+
format: format,
2853+
fieldFilePath: fieldFilePath,
2854+
})
2855+
coll := client.Database(dbName).Collection(format + "testcoll")
2856+
var bobDoc bson.M
2857+
err := coll.FindOne(t.Context(), bson.D{{"a", "bob"}}).Decode(&bobDoc)
2858+
require.NoError(t, err)
2859+
assert.Equal(
2860+
t, "", bobDoc["b"],
2861+
"%s: blank field should be empty string without --ignoreBlanks", format,
2862+
)
2863+
})
2864+
2865+
t.Run(format+"/ignoreBlanks", func(t *testing.T) {
2866+
importAndCheckFields(t, client, importFieldsOpts{
2867+
dbName: dbName,
2868+
inputFile: noHeaderFile,
2869+
format: format,
2870+
fieldFilePath: fieldFilePath,
2871+
ignoreBlanks: true,
2872+
})
2873+
coll := client.Database(dbName).Collection(format + "testcoll")
2874+
var bobDoc bson.M
2875+
err := coll.FindOne(t.Context(), bson.D{{"a", "bob"}}).Decode(&bobDoc)
2876+
require.NoError(t, err)
2877+
_, hasB := bobDoc["b"]
2878+
assert.False(t, hasB, "%s: blank field should be omitted with --ignoreBlanks", format)
2879+
})
2880+
2881+
t.Run(format+"/noFieldSpec", func(t *testing.T) {
2882+
toolOpts, err := testutil.GetToolOptions()
2883+
require.NoError(t, err)
2884+
toolOpts.Namespace = &options.Namespace{DB: dbName, Collection: format + "testcoll"}
2885+
_, err = New(Options{
2886+
ToolOptions: toolOpts,
2887+
InputOptions: &InputOptions{File: noHeaderFile, Type: format, ParseGrace: "stop"},
2888+
IngestOptions: &IngestOptions{},
2889+
})
2890+
assert.Error(t, err, "%s: import without field spec should fail", format)
2891+
})
2892+
}
2893+
2894+
type importFieldsOpts struct {
2895+
dbName string
2896+
inputFile string
2897+
format string
2898+
fieldFilePath string
2899+
fields *string
2900+
headerLine bool
2901+
ignoreBlanks bool
2902+
}
2903+
2904+
func importAndCheckFields(t *testing.T, client *mongo.Client, o importFieldsOpts) {
2905+
t.Helper()
2906+
collName := o.format + "testcoll"
2907+
require.NoError(t, client.Database(o.dbName).Collection(collName).Drop(t.Context()))
2908+
toolOpts, err := testutil.GetToolOptions()
2909+
require.NoError(t, err)
2910+
toolOpts.Namespace = &options.Namespace{DB: o.dbName, Collection: collName}
2911+
var ffPtr *string
2912+
if o.fieldFilePath != "" {
2913+
ffPtr = &o.fieldFilePath
2914+
}
2915+
mi, err := New(Options{
2916+
ToolOptions: toolOpts,
2917+
InputOptions: &InputOptions{
2918+
File: o.inputFile,
2919+
Type: o.format,
2920+
HeaderLine: o.headerLine,
2921+
FieldFile: ffPtr,
2922+
Fields: o.fields,
2923+
ParseGrace: "stop",
2924+
},
2925+
IngestOptions: &IngestOptions{IgnoreBlanks: o.ignoreBlanks},
2926+
})
2927+
require.NoError(t, err)
2928+
_, _, err = mi.ImportDocuments()
2929+
require.NoError(t, err)
2930+
2931+
coll := client.Database(o.dbName).Collection(collName)
2932+
n, err := coll.CountDocuments(t.Context(), bson.D{})
2933+
require.NoError(t, err)
2934+
assert.EqualValues(t, 3, n, "%s: should import 3 documents", o.format)
2935+
2936+
nestedQuery := bson.D{
2937+
{"a", "foo"},
2938+
{"b", "bar"},
2939+
{"c.xyz", "blah"},
2940+
{"d.hij.lkm", "qwz"},
2941+
}
2942+
n, err = coll.CountDocuments(t.Context(), nestedQuery)
2943+
require.NoError(t, err)
2944+
assert.EqualValues(t, 1, n, "%s: nested fields should be stored correctly", o.format)
2945+
}
2946+
2947+
// TestImportExtraFields verifies that CSV columns beyond the fieldFile mapping
2948+
// are imported as field4, field5, etc.
2949+
func TestImportExtraFields(t *testing.T) {
2950+
testtype.SkipUnlessTestType(t, testtype.IntegrationTestType)
2951+
2952+
const dbName = "mongoimport_extrafields_test"
2953+
const collName = "extrafields"
2954+
2955+
sessionProvider, _, err := testutil.GetBareSessionProvider()
2956+
require.NoError(t, err)
2957+
client, err := sessionProvider.GetSession()
2958+
require.NoError(t, err)
2959+
t.Cleanup(func() {
2960+
if err := client.Database(dbName).Drop(context.Background()); err != nil {
2961+
t.Errorf("dropping test database: %v", err)
2962+
}
2963+
})
2964+
2965+
tmpDir := t.TempDir()
2966+
extraRows := [][]string{
2967+
{"foo", "bar", "blah", "qwz"},
2968+
{"bob", "", "steve", "sue"},
2969+
{"one", "two", "three", "four", "extra1", "extra2", "extra3"},
2970+
}
2971+
extraFile := filepath.Join(tmpDir, "extrafields.csv")
2972+
writeXSVFile(t, extraFile, ',', extraRows)
2973+
2974+
fieldNames := []string{"a", "b", "c.xyz", "d.hij.lkm"}
2975+
fieldFilePath := writeFieldFile(t, tmpDir, "fieldfile", fieldNames)
2976+
2977+
toolOpts, err := testutil.GetToolOptions()
2978+
require.NoError(t, err)
2979+
toolOpts.Namespace = &options.Namespace{DB: dbName, Collection: collName}
2980+
mi, err := New(Options{
2981+
ToolOptions: toolOpts,
2982+
InputOptions: &InputOptions{
2983+
File: extraFile,
2984+
Type: "csv",
2985+
FieldFile: &fieldFilePath,
2986+
ParseGrace: "stop",
2987+
},
2988+
IngestOptions: &IngestOptions{},
2989+
})
2990+
require.NoError(t, err)
2991+
_, _, err = mi.ImportDocuments()
2992+
require.NoError(t, err)
2993+
2994+
var doc bson.M
2995+
err = client.Database(dbName).Collection(collName).
2996+
FindOne(t.Context(), bson.D{{"a", "one"}}).Decode(&doc)
2997+
require.NoError(t, err)
2998+
assert.Equal(t, "extra1", doc["field4"], "field4 should contain first extra value")
2999+
assert.Equal(t, "extra2", doc["field5"], "field5 should contain second extra value")
3000+
assert.Equal(t, "extra3", doc["field6"], "field6 should contain third extra value")
3001+
}
3002+
3003+
func writeXSVFile(t *testing.T, path string, separator rune, records [][]string) {
3004+
t.Helper()
3005+
f, err := os.Create(path)
3006+
require.NoError(t, err)
3007+
w := csv.NewWriter(f)
3008+
w.Comma = separator
3009+
require.NoError(t, w.WriteAll(records))
3010+
require.NoError(t, f.Close())
3011+
}
3012+
3013+
func writeFieldFile(t *testing.T, dir, name string, fields []string) string {
3014+
t.Helper()
3015+
path := filepath.Join(dir, name)
3016+
require.NoError(t, os.WriteFile(path, []byte(strings.Join(fields, "\n")+"\n"), 0o644))
3017+
return path
3018+
}

test/qa-tests/jstests/import/fields.js

Lines changed: 0 additions & 107 deletions
This file was deleted.

0 commit comments

Comments
 (0)