|
| 1 | +/* |
| 2 | +** Copyright (c) 2025 Oracle and/or its affiliates. |
| 3 | +** |
| 4 | +** The Universal Permissive License (UPL), Version 1.0 |
| 5 | +** |
| 6 | +** Subject to the condition set forth below, permission is hereby granted to any |
| 7 | +** person obtaining a copy of this software, associated documentation and/or data |
| 8 | +** (collectively the "Software"), free of charge and under any and all copyright |
| 9 | +** rights in the Software, and any and all patent rights owned or freely |
| 10 | +** licensable by each licensor hereunder covering either (i) the unmodified |
| 11 | +** Software as contributed to or provided by such licensor, or (ii) the Larger |
| 12 | +** Works (as defined below), to deal in both |
| 13 | +** |
| 14 | +** (a) the Software, and |
| 15 | +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if |
| 16 | +** one is included with the Software (each a "Larger Work" to which the Software |
| 17 | +** is contributed by such licensors), |
| 18 | +** |
| 19 | +** without restriction, including without limitation the rights to copy, create |
| 20 | +** derivative works of, display, perform, and distribute the Software and make, |
| 21 | +** use, sell, offer for sale, import, export, have made, and have sold the |
| 22 | +** Software and the Larger Work(s), and to sublicense the foregoing rights on |
| 23 | +** either these or other terms. |
| 24 | +** |
| 25 | +** This license is subject to the following condition: |
| 26 | +** The above copyright notice and either this complete permission notice or at |
| 27 | +** a minimum a reference to the UPL must be included in all copies or |
| 28 | +** substantial portions of the Software. |
| 29 | +** |
| 30 | +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 31 | +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 32 | +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 33 | +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 34 | +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 35 | +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 36 | +** SOFTWARE. |
| 37 | + */ |
| 38 | + |
| 39 | +package tests |
| 40 | + |
| 41 | +import ( |
| 42 | + "testing" |
| 43 | + "time" |
| 44 | + |
| 45 | + "gorm.io/gorm" |
| 46 | +) |
| 47 | + |
| 48 | +type DateModel struct { |
| 49 | + ID uint `gorm:"primaryKey;autoIncrement"` |
| 50 | + EventName string `gorm:"size:100"` |
| 51 | + EventDate time.Time `gorm:"column:EVENT_DATE;type:DATE"` |
| 52 | + OptionalCol *time.Time `gorm:"column:OPTIONAL_DATE"` |
| 53 | +} |
| 54 | + |
| 55 | +func setupDateTests(t *testing.T) { |
| 56 | + t.Log("Setting up DATE test table") |
| 57 | + |
| 58 | + // Drop the table safely |
| 59 | + dropSQL := ` |
| 60 | + BEGIN |
| 61 | + EXECUTE IMMEDIATE 'DROP TABLE "date_models" CASCADE CONSTRAINTS'; |
| 62 | + EXCEPTION |
| 63 | + WHEN OTHERS THEN |
| 64 | + IF SQLCODE != -942 THEN RAISE; END IF; |
| 65 | + END;` |
| 66 | + if err := DB.Exec(dropSQL).Error; err != nil { |
| 67 | + t.Fatalf("Failed to drop table: %v", err) |
| 68 | + } |
| 69 | + |
| 70 | + // migrate |
| 71 | + if err := DB.AutoMigrate(&DateModel{}); err != nil { |
| 72 | + t.Fatalf("Failed to migrate date_models: %v", err) |
| 73 | + } |
| 74 | + |
| 75 | + t.Log("DATE test table created successfully") |
| 76 | +} |
| 77 | + |
| 78 | +func TestDate_InsertAndRetrieve(t *testing.T) { |
| 79 | + setupDateTests(t) |
| 80 | + |
| 81 | + now := time.Now().Truncate(time.Second) |
| 82 | + record := DateModel{ |
| 83 | + EventName: "Launch", |
| 84 | + EventDate: now, |
| 85 | + } |
| 86 | + if err := DB.Create(&record).Error; err != nil { |
| 87 | + t.Fatalf("Insert failed: %v", err) |
| 88 | + } |
| 89 | + |
| 90 | + var out DateModel |
| 91 | + err := DB.First(&out, record.ID).Error |
| 92 | + if err != nil { |
| 93 | + t.Fatalf("Fetch failed: %v", err) |
| 94 | + } |
| 95 | + |
| 96 | + if !out.EventDate.Equal(now) { |
| 97 | + t.Errorf("Expected %v, got %v", now, out.EventDate) |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +func TestDate_NullAndOptionalColumns(t *testing.T) { |
| 102 | + setupDateTests(t) |
| 103 | + |
| 104 | + record := DateModel{ |
| 105 | + EventName: "NullDate", |
| 106 | + } |
| 107 | + if err := DB.Create(&record).Error; err != nil { |
| 108 | + t.Fatalf("Insert with null date failed: %v", err) |
| 109 | + } |
| 110 | + |
| 111 | + var fetched DateModel |
| 112 | + err := DB.First(&fetched, record.ID).Error |
| 113 | + if err != nil { |
| 114 | + t.Fatalf("Fetch failed: %v", err) |
| 115 | + } |
| 116 | + |
| 117 | + if fetched.OptionalCol != nil { |
| 118 | + t.Errorf("Expected OptionalCol to be nil, got %v", fetched.OptionalCol) |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +func TestDate_UpdateAndCompare(t *testing.T) { |
| 123 | + setupDateTests(t) |
| 124 | + |
| 125 | + initial := DateModel{ |
| 126 | + EventName: "CompareTest", |
| 127 | + EventDate: time.Date(2025, 10, 30, 10, 10, 10, 0, time.UTC), |
| 128 | + } |
| 129 | + if err := DB.Create(&initial).Error; err != nil { |
| 130 | + t.Fatalf("Create failed: %v", err) |
| 131 | + } |
| 132 | + |
| 133 | + updateTime := initial.EventDate.Add(2 * time.Hour) |
| 134 | + if err := DB.Model(&initial).Update("EVENT_DATE", updateTime).Error; err != nil { |
| 135 | + t.Fatalf("Update failed: %v", err) |
| 136 | + } |
| 137 | + |
| 138 | + var fetched DateModel |
| 139 | + err := DB.First(&fetched, initial.ID).Error |
| 140 | + if err != nil { |
| 141 | + t.Fatalf("Fetch failed: %v", err) |
| 142 | + } |
| 143 | + |
| 144 | + if !fetched.EventDate.Equal(updateTime) { |
| 145 | + t.Errorf("Expected %v, got %v", updateTime, fetched.EventDate) |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +func TestDate_QueryByDateRange(t *testing.T) { |
| 150 | + setupDateTests(t) |
| 151 | + |
| 152 | + base := time.Now().Truncate(time.Second) |
| 153 | + records := []DateModel{ |
| 154 | + {EventName: "Day1", EventDate: base.Add(-24 * time.Hour)}, |
| 155 | + {EventName: "Day2", EventDate: base}, |
| 156 | + {EventName: "Day3", EventDate: base.Add(24 * time.Hour)}, |
| 157 | + } |
| 158 | + if err := DB.Create(&records).Error; err != nil { |
| 159 | + t.Fatalf("Bulk insert failed: %v", err) |
| 160 | + } |
| 161 | + |
| 162 | + var results []DateModel |
| 163 | + err := DB.Where("\"EVENT_DATE\" BETWEEN ? AND ?", base.Add(-12*time.Hour), base.Add(12*time.Hour)).Find(&results).Error |
| 164 | + if err != nil { |
| 165 | + t.Fatalf("Range query failed: %v", err) |
| 166 | + } |
| 167 | + |
| 168 | + if len(results) != 1 { |
| 169 | + t.Errorf("Expected 1 record, got %d", len(results)) |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +func TestDate_InvalidDateHandling(t *testing.T) { |
| 174 | + setupDateTests(t) |
| 175 | + |
| 176 | + // insert invalid date |
| 177 | + err := DB.Exec(`INSERT INTO "DATE_MODELS" ("EVENT_NAME","EVENT_DATE") VALUES ('BadDate', TO_DATE('2025-13-40','YYYY-MM-DD'))`).Error |
| 178 | + if err == nil { |
| 179 | + t.Error("Expected error inserting invalid date, got nil") |
| 180 | + } |
| 181 | +} |
| 182 | + |
| 183 | +func TestDate_TimePrecisionLoss(t *testing.T) { |
| 184 | + setupDateTests(t) |
| 185 | + |
| 186 | + withMillis := time.Now().Truncate(time.Millisecond) |
| 187 | + record := DateModel{ |
| 188 | + EventName: "PrecisionLoss", |
| 189 | + EventDate: withMillis, |
| 190 | + } |
| 191 | + if err := DB.Create(&record).Error; err != nil { |
| 192 | + t.Fatalf("Insert failed: %v", err) |
| 193 | + } |
| 194 | + |
| 195 | + var fetched DateModel |
| 196 | + _ = DB.First(&fetched, record.ID).Error |
| 197 | + if !fetched.EventDate.Truncate(time.Second).Equal(withMillis.Truncate(time.Second)) { |
| 198 | + t.Errorf("Expected time match up to seconds precision, got %v", fetched.EventDate) |
| 199 | + } |
| 200 | +} |
| 201 | + |
| 202 | +func TestDate_ZeroDateHandling(t *testing.T) { |
| 203 | + setupDateTests(t) |
| 204 | + |
| 205 | + zeroTime := time.Time{} |
| 206 | + record := DateModel{ |
| 207 | + EventName: "ZeroDate", |
| 208 | + EventDate: zeroTime, |
| 209 | + } |
| 210 | + err := DB.Create(&record).Error |
| 211 | + if err != nil && err != gorm.ErrInvalidData { |
| 212 | + t.Errorf("Unexpected error inserting zero date: %v", err) |
| 213 | + } |
| 214 | +} |
0 commit comments