F1: ValidateFileContent rejected the pipeline's own output. GDAL writes a
BigTIFF header ("II+\0" / "MM\0+") whenever BIGTIFF=IF_SAFER kicks in for a
large raster, so accept both alongside the classic TIFF magic and reword the
error to "file is not a valid TIFF/BigTIFF GeoTIFF". GodalIO.Create now
delegates to an unexported createGTiff so a test can force BIGTIFF=YES and
assert the produced header passes the check.
F3: a user-supplied filename went straight into the S3 object key for dataset
uploads and for both dataset and dataset-group SLD uploads, so "../../x.sld"
escaped the dataset's own prefix. Reduce every such name to its base with a
shared safeObjectName helper, rejecting names with no usable base.
F9: rename the shadowing inner "raw" in the PATCH dg_id case to "s".
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
104 lines
2.8 KiB
Go
104 lines
2.8 KiB
Go
package raster
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"gis/internal/domain"
|
|
)
|
|
|
|
func TestGodalIO_RoundTrip(t *testing.T) {
|
|
rio := NewGodalIO()
|
|
ctx := context.Background()
|
|
path := filepath.Join(t.TempDir(), "out.tif")
|
|
grid := Grid{Width: 10, Height: 7, GeoTransform: [6]float64{100, 30, 0, 200, 0, -30}, ProjectionWKT: "EPSG:32642"}
|
|
|
|
w, err := rio.Create(ctx, path, grid, DefaultWriterOptions())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
full := make([]int16, 10*7)
|
|
for i := range full {
|
|
full[i] = int16(i%6) - 1 // -1..4 (-1 stands in for garbage, not nodata)
|
|
}
|
|
full[0] = -9999
|
|
if err := w.WriteInt16(Window{0, 0, 10, 7}, full); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := w.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r, err := rio.Open(ctx, path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer r.Close()
|
|
g := r.Grid()
|
|
if g.Width != 10 || g.Height != 7 || g.GeoTransform != grid.GeoTransform || g.ProjectionWKT == "" {
|
|
t.Fatalf("grid mismatch: %+v", g)
|
|
}
|
|
if nd, ok := r.NoData(); !ok || nd != -9999 {
|
|
t.Fatalf("nodata = %v %v", nd, ok)
|
|
}
|
|
dst := make([]float64, 3*2)
|
|
if err := r.ReadFloat64(Window{X: 8, Y: 5, Width: 2, Height: 2}, dst[:4]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// pixel (8,5) has index 58 -> 58%6-1 = 3 ; (9,5)=59 -> 4 ; (8,6)=68 -> 1 ; (9,6)=69 -> 2
|
|
want := []float64{3, 4, 1, 2}
|
|
for i, v := range want {
|
|
if dst[i] != v {
|
|
t.Fatalf("pixel %d = %v want %v", i, dst[i], v)
|
|
}
|
|
}
|
|
if !rio.SameGrid(g, grid) {
|
|
t.Fatal("SameGrid should be true for identical grids")
|
|
}
|
|
other := grid
|
|
other.GeoTransform[0] += 1
|
|
if rio.SameGrid(g, other) {
|
|
t.Fatal("SameGrid should be false for shifted origin")
|
|
}
|
|
}
|
|
|
|
// TestGodalIO_BigTIFFPassesContentValidation guards the upload magic-byte
|
|
// check against the pipeline's own output: GDAL writes a BigTIFF header
|
|
// (version 43) whenever BIGTIFF is YES or IF_SAFER kicks in for a large
|
|
// raster, and domain.ValidateFileContent must accept it.
|
|
func TestGodalIO_BigTIFFPassesContentValidation(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "big.tif")
|
|
grid := Grid{Width: 8, Height: 8, GeoTransform: [6]float64{100, 30, 0, 200, 0, -30}, ProjectionWKT: "EPSG:32642"}
|
|
|
|
w, err := createGTiff(path, grid, DefaultWriterOptions(), "YES")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := w.WriteInt16(Window{0, 0, 8, 8}, make([]int16, 8*8)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := w.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
head := make([]byte, 4)
|
|
if _, err := io.ReadFull(f, head); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.HasPrefix(head, []byte("II+\x00")) && !bytes.HasPrefix(head, []byte("MM\x00+")) {
|
|
t.Fatalf("BIGTIFF=YES did not produce a BigTIFF header: % x", head)
|
|
}
|
|
if err := domain.ValidateFileContent(".tif", head); err != nil {
|
|
t.Fatalf("ValidateFileContent rejected our own BigTIFF output: %v", err)
|
|
}
|
|
}
|