|
| 1 | +// Copyright 2021-present The Atlas Authors. All rights reserved. |
| 2 | +// This source code is licensed under the Apache 2.0 license found |
| 3 | +// in the LICENSE file in the root directory of this source tree. |
| 4 | + |
| 5 | +//go:build !ent |
| 6 | + |
| 7 | +package ydb |
| 8 | + |
| 9 | +import ( |
| 10 | + "testing" |
| 11 | + |
| 12 | + "ariga.io/atlas/sql/schema" |
| 13 | + "github.com/stretchr/testify/require" |
| 14 | +) |
| 15 | + |
| 16 | +func TestConvert_FormatType(t *testing.T) { |
| 17 | + tests := []struct { |
| 18 | + name string |
| 19 | + typ schema.Type |
| 20 | + expected string |
| 21 | + wantErr bool |
| 22 | + }{ |
| 23 | + // Boolean type |
| 24 | + {name: "bool", typ: &schema.BoolType{T: TypeBool}, expected: TypeBool}, |
| 25 | + |
| 26 | + // Integer types (signed) |
| 27 | + {name: "int8", typ: &schema.IntegerType{T: TypeInt8}, expected: TypeInt8}, |
| 28 | + {name: "int16", typ: &schema.IntegerType{T: TypeInt16}, expected: TypeInt16}, |
| 29 | + {name: "int32", typ: &schema.IntegerType{T: TypeInt32}, expected: TypeInt32}, |
| 30 | + {name: "int64", typ: &schema.IntegerType{T: TypeInt64}, expected: TypeInt64}, |
| 31 | + |
| 32 | + // Integer types (unsigned) |
| 33 | + {name: "uint8", typ: &schema.IntegerType{T: TypeUint8, Unsigned: true}, expected: TypeUint8}, |
| 34 | + {name: "uint16", typ: &schema.IntegerType{T: TypeUint16, Unsigned: true}, expected: TypeUint16}, |
| 35 | + {name: "uint32", typ: &schema.IntegerType{T: TypeUint32, Unsigned: true}, expected: TypeUint32}, |
| 36 | + {name: "uint64", typ: &schema.IntegerType{T: TypeUint64, Unsigned: true}, expected: TypeUint64}, |
| 37 | + |
| 38 | + // Integer types (signed to unsigned conversion) |
| 39 | + {name: "int8_to_uint8", typ: &schema.IntegerType{T: TypeInt8, Unsigned: true}, expected: TypeUint8}, |
| 40 | + {name: "int16_to_uint16", typ: &schema.IntegerType{T: TypeInt16, Unsigned: true}, expected: TypeUint16}, |
| 41 | + {name: "int32_to_uint32", typ: &schema.IntegerType{T: TypeInt32, Unsigned: true}, expected: TypeUint32}, |
| 42 | + {name: "int64_to_uint64", typ: &schema.IntegerType{T: TypeInt64, Unsigned: true}, expected: TypeUint64}, |
| 43 | + |
| 44 | + // Float types |
| 45 | + {name: "float", typ: &schema.FloatType{T: TypeFloat}, expected: TypeFloat}, |
| 46 | + {name: "double", typ: &schema.FloatType{T: TypeDouble}, expected: TypeDouble}, |
| 47 | + |
| 48 | + // Decimal types |
| 49 | + {name: "decimal_with_precision_scale", typ: &schema.DecimalType{T: TypeDecimal, Precision: 10, Scale: 2}, expected: "decimal(10,2)"}, |
| 50 | + {name: "decimal_with_precision_zero_scale", typ: &schema.DecimalType{T: TypeDecimal, Precision: 10, Scale: 0}, expected: "decimal(10,0)"}, |
| 51 | + {name: "decimal_max_precision", typ: &schema.DecimalType{T: TypeDecimal, Precision: 35, Scale: 10}, expected: "decimal(35,10)"}, |
| 52 | + {name: "decimal_invalid_precision_zero", typ: &schema.DecimalType{T: TypeDecimal, Precision: 0, Scale: 0}, wantErr: true}, |
| 53 | + {name: "decimal_invalid_precision_too_large", typ: &schema.DecimalType{T: TypeDecimal, Precision: 36, Scale: 0}, wantErr: true}, |
| 54 | + |
| 55 | + // Serial types |
| 56 | + {name: "smallserial", typ: &SerialType{T: TypeSmallSerial}, expected: TypeSmallSerial}, |
| 57 | + {name: "serial2", typ: &SerialType{T: TypeSerial2}, expected: TypeSerial2}, |
| 58 | + {name: "serial", typ: &SerialType{T: TypeSerial}, expected: TypeSerial}, |
| 59 | + {name: "serial4", typ: &SerialType{T: TypeSerial4}, expected: TypeSerial4}, |
| 60 | + {name: "serial8", typ: &SerialType{T: TypeSerial8}, expected: TypeSerial8}, |
| 61 | + {name: "bigserial", typ: &SerialType{T: TypeBigSerial}, expected: TypeBigSerial}, |
| 62 | + |
| 63 | + // String/Binary types |
| 64 | + {name: "string", typ: &schema.BinaryType{T: TypeString}, expected: TypeString}, |
| 65 | + {name: "utf8", typ: &schema.StringType{T: TypeUtf8}, expected: TypeUtf8}, |
| 66 | + |
| 67 | + // JSON types |
| 68 | + {name: "json", typ: &schema.JSONType{T: TypeJson}, expected: TypeJson}, |
| 69 | + {name: "jsondocument", typ: &schema.JSONType{T: TypeJsonDocument}, expected: TypeJsonDocument}, |
| 70 | + |
| 71 | + // YSON type |
| 72 | + {name: "yson", typ: YsonType{T: TypeYson}, expected: TypeYson}, |
| 73 | + |
| 74 | + // UUID type |
| 75 | + {name: "uuid", typ: &schema.UUIDType{T: TypeUuid}, expected: TypeUuid}, |
| 76 | + |
| 77 | + // Date/Time types |
| 78 | + {name: "date", typ: &schema.TimeType{T: TypeDate}, expected: TypeDate}, |
| 79 | + {name: "date32", typ: &schema.TimeType{T: TypeDate32}, expected: TypeDate32}, |
| 80 | + {name: "datetime", typ: &schema.TimeType{T: TypeDateTime}, expected: TypeDateTime}, |
| 81 | + {name: "datetime64", typ: &schema.TimeType{T: TypeDateTime64}, expected: TypeDateTime64}, |
| 82 | + {name: "timestamp", typ: &schema.TimeType{T: TypeTimestamp}, expected: TypeTimestamp}, |
| 83 | + {name: "timestamp64", typ: &schema.TimeType{T: TypeTimestamp64}, expected: TypeTimestamp64}, |
| 84 | + {name: "interval", typ: &schema.TimeType{T: TypeInterval}, expected: TypeInterval}, |
| 85 | + {name: "interval64", typ: &schema.TimeType{T: TypeInterval64}, expected: TypeInterval64}, |
| 86 | + |
| 87 | + // Timezone-aware date/time types |
| 88 | + {name: "tzdate", typ: &schema.TimeType{T: TypeTzDate}, expected: TypeTzDate}, |
| 89 | + {name: "tzdate32", typ: &schema.TimeType{T: TypeTzDate32}, expected: TypeTzDate32}, |
| 90 | + {name: "tzdatetime", typ: &schema.TimeType{T: TypeTzDateTime}, expected: TypeTzDateTime}, |
| 91 | + {name: "tzdatetime64", typ: &schema.TimeType{T: TypeTzDateTime64}, expected: TypeTzDateTime64}, |
| 92 | + {name: "tztimestamp", typ: &schema.TimeType{T: TypeTzTimestamp}, expected: TypeTzTimestamp}, |
| 93 | + {name: "tztimestamp64", typ: &schema.TimeType{T: TypeTzTimestamp64}, expected: TypeTzTimestamp64}, |
| 94 | + |
| 95 | + // Optional type |
| 96 | + {name: "optional_int32", typ: OptionalType{T: "Optional<int32>", InnerType: &schema.IntegerType{T: TypeInt32}}, expected: "Optional<int32>"}, |
| 97 | + |
| 98 | + // Error cases |
| 99 | + {name: "unsupported_type", typ: &schema.UnsupportedType{T: "unknown"}, wantErr: true}, |
| 100 | + {name: "unsupported_integer_type", typ: &schema.IntegerType{T: "unsupported"}, wantErr: true}, |
| 101 | + {name: "unsupported_float_type", typ: &schema.FloatType{T: "unsupported"}, wantErr: true}, |
| 102 | + {name: "unsupported_json_type", typ: &schema.JSONType{T: "unsupported"}, wantErr: true}, |
| 103 | + {name: "unsupported_time_type", typ: &schema.TimeType{T: "unsupported"}, wantErr: true}, |
| 104 | + } |
| 105 | + |
| 106 | + for _, tt := range tests { |
| 107 | + t.Run(tt.name, func(t *testing.T) { |
| 108 | + result, err := FormatType(tt.typ) |
| 109 | + if tt.wantErr { |
| 110 | + require.Error(t, err) |
| 111 | + } else { |
| 112 | + require.NoError(t, err) |
| 113 | + require.Equal(t, tt.expected, result) |
| 114 | + } |
| 115 | + }) |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +func TestConvert_ParseType(t *testing.T) { |
| 120 | + tests := []struct { |
| 121 | + name string |
| 122 | + input string |
| 123 | + expected schema.Type |
| 124 | + wantErr bool |
| 125 | + }{ |
| 126 | + // Boolean type |
| 127 | + {name: "bool", input: TypeBool, expected: &schema.BoolType{T: TypeBool}}, |
| 128 | + |
| 129 | + // Integer types (signed) |
| 130 | + {name: "int8", input: TypeInt8, expected: &schema.IntegerType{T: TypeInt8, Unsigned: false}}, |
| 131 | + {name: "int16", input: TypeInt16, expected: &schema.IntegerType{T: TypeInt16, Unsigned: false}}, |
| 132 | + {name: "int32", input: TypeInt32, expected: &schema.IntegerType{T: TypeInt32, Unsigned: false}}, |
| 133 | + {name: "int64", input: TypeInt64, expected: &schema.IntegerType{T: TypeInt64, Unsigned: false}}, |
| 134 | + |
| 135 | + // Integer types (unsigned) |
| 136 | + {name: "uint8", input: TypeUint8, expected: &schema.IntegerType{T: TypeUint8, Unsigned: true}}, |
| 137 | + {name: "uint16", input: TypeUint16, expected: &schema.IntegerType{T: TypeUint16, Unsigned: true}}, |
| 138 | + {name: "uint32", input: TypeUint32, expected: &schema.IntegerType{T: TypeUint32, Unsigned: true}}, |
| 139 | + {name: "uint64", input: TypeUint64, expected: &schema.IntegerType{T: TypeUint64, Unsigned: true}}, |
| 140 | + |
| 141 | + // Float types |
| 142 | + {name: "float", input: TypeFloat, expected: &schema.FloatType{T: TypeFloat, Precision: 24}}, |
| 143 | + {name: "double", input: TypeDouble, expected: &schema.FloatType{T: TypeDouble, Precision: 53}}, |
| 144 | + |
| 145 | + // Decimal types |
| 146 | + {name: "decimal", input: "decimal(10,2)", expected: &schema.DecimalType{T: TypeDecimal, Precision: 10, Scale: 2}}, |
| 147 | + {name: "decimal_max_precision", input: "decimal(35,10)", expected: &schema.DecimalType{T: TypeDecimal, Precision: 35, Scale: 10}}, |
| 148 | + {name: "decimal_zero_scale", input: "decimal(10,0)", expected: &schema.DecimalType{T: TypeDecimal, Precision: 10, Scale: 0}}, |
| 149 | + |
| 150 | + // Serial types |
| 151 | + {name: "smallserial", input: TypeSmallSerial, expected: &SerialType{T: TypeSmallSerial}}, |
| 152 | + {name: "serial2", input: TypeSerial2, expected: &SerialType{T: TypeSerial2}}, |
| 153 | + {name: "serial", input: TypeSerial, expected: &SerialType{T: TypeSerial}}, |
| 154 | + {name: "serial4", input: TypeSerial4, expected: &SerialType{T: TypeSerial4}}, |
| 155 | + {name: "serial8", input: TypeSerial8, expected: &SerialType{T: TypeSerial8}}, |
| 156 | + {name: "bigserial", input: TypeBigSerial, expected: &SerialType{T: TypeBigSerial}}, |
| 157 | + |
| 158 | + // String/Binary types |
| 159 | + {name: "string", input: TypeString, expected: &schema.BinaryType{T: TypeString}}, |
| 160 | + {name: "utf8", input: TypeUtf8, expected: &schema.StringType{T: TypeUtf8}}, |
| 161 | + |
| 162 | + // JSON types |
| 163 | + {name: "json", input: TypeJson, expected: &schema.JSONType{T: TypeJson}}, |
| 164 | + {name: "jsonDocument", input: TypeJsonDocument, expected: &schema.JSONType{T: TypeJsonDocument}}, |
| 165 | + |
| 166 | + // YSON type |
| 167 | + {name: "yson", input: TypeYson, expected: &YsonType{T: TypeYson}}, |
| 168 | + |
| 169 | + // UUID type |
| 170 | + {name: "uuid", input: TypeUuid, expected: &schema.UUIDType{T: TypeUuid}}, |
| 171 | + |
| 172 | + // Date/Time types |
| 173 | + {name: "date", input: TypeDate, expected: &schema.TimeType{T: TypeDate}}, |
| 174 | + {name: "date32", input: TypeDate32, expected: &schema.TimeType{T: TypeDate32}}, |
| 175 | + {name: "datetime", input: TypeDateTime, expected: &schema.TimeType{T: TypeDateTime}}, |
| 176 | + {name: "datetime64", input: TypeDateTime64, expected: &schema.TimeType{T: TypeDateTime64}}, |
| 177 | + {name: "timestamp", input: TypeTimestamp, expected: &schema.TimeType{T: TypeTimestamp}}, |
| 178 | + {name: "timestamp64", input: TypeTimestamp64, expected: &schema.TimeType{T: TypeTimestamp64}}, |
| 179 | + {name: "interval", input: TypeInterval, expected: &schema.TimeType{T: TypeInterval}}, |
| 180 | + {name: "interval64", input: TypeInterval64, expected: &schema.TimeType{T: TypeInterval64}}, |
| 181 | + |
| 182 | + // Timezone-aware date/time types |
| 183 | + {name: "tzdate", input: TypeTzDate, expected: &schema.TimeType{T: TypeTzDate}}, |
| 184 | + {name: "tzdate32", input: TypeTzDate32, expected: &schema.TimeType{T: TypeTzDate32}}, |
| 185 | + {name: "tzdatetime", input: TypeTzDateTime, expected: &schema.TimeType{T: TypeTzDateTime}}, |
| 186 | + {name: "tzdatetime64", input: TypeTzDateTime64, expected: &schema.TimeType{T: TypeTzDateTime64}}, |
| 187 | + {name: "tztimestamp", input: TypeTzTimestamp, expected: &schema.TimeType{T: TypeTzTimestamp}}, |
| 188 | + {name: "tztimestamp64", input: TypeTzTimestamp64, expected: &schema.TimeType{T: TypeTzTimestamp64}}, |
| 189 | + |
| 190 | + // Optional types |
| 191 | + {name: "optional_int32", input: "Optional<int32>", expected: &OptionalType{T: "Optional<int32>", InnerType: &schema.IntegerType{T: TypeInt32, Unsigned: false}}}, |
| 192 | + {name: "optional_utf8", input: "Optional<utf8>", expected: &OptionalType{T: "Optional<utf8>", InnerType: &schema.StringType{T: TypeUtf8}}}, |
| 193 | + {name: "optional_bool", input: "Optional<bool>", expected: &OptionalType{T: "Optional<bool>", InnerType: &schema.BoolType{T: TypeBool}}}, |
| 194 | + |
| 195 | + // Unsupported/unknown types |
| 196 | + {name: "unknown_type", input: "unknown", expected: &schema.UnsupportedType{T: "unknown"}}, |
| 197 | + |
| 198 | + // Error cases |
| 199 | + {name: "empty_string", input: "", wantErr: true}, |
| 200 | + {name: "invalid_decimal_missing_params", input: "decimal", wantErr: true}, |
| 201 | + {name: "invalid_decimal_precision", input: "decimal(100,2)", wantErr: true}, |
| 202 | + } |
| 203 | + |
| 204 | + for _, tt := range tests { |
| 205 | + t.Run(tt.name, func(t *testing.T) { |
| 206 | + result, err := ParseType(tt.input) |
| 207 | + if tt.wantErr { |
| 208 | + require.Error(t, err) |
| 209 | + } else { |
| 210 | + require.NoError(t, err) |
| 211 | + require.Equal(t, tt.expected, result) |
| 212 | + } |
| 213 | + }) |
| 214 | + } |
| 215 | +} |
0 commit comments